0

I have a partial NGINX config file that gets pulled into a main NGINX config file automatically by AWS Elastic Beanstalk. I have two instances running, one EC2 web server running PHP and another that's an SQS worker.

I had set up my config file to force HTTPS using:

location / {
    try_files $uri $uri/ /index.php?$query_string;
    gzip_static on;

    return 301 https://$host$request_uri;
}

This worked great for forcing HTTPS, but I was running into issues with both the ELB health monitor failing due to a 301 (expected 200), and the SQS queue also failing due to returning a 301 -- the queue is triggered by a POST from a configured cron job, which seemed to not work with the 301 redirect:

version: 1
cron:
 - name: "schedule"
   url: "/worker/schedule"
   schedule: "* * * * *"

This is using a package that listens for the POST request to that URL and then kicks off jobs.

To fix those issues, I tried this to check for the appropriate headers and turn off the redirect:

location / {
    try_files $uri $uri/ /index.php?$query_string;
    gzip_static on;

    set $redirect_to_https 0;
    if ($http_x_forwarded_proto != 'https') {
        set $redirect_to_https 1;
    }

    if ($http_user_agent ~* '^ELB-HealthChecker\/.*$') {
        access_log off;
        set $redirect_to_https 0;
    }

    if ($http_user_agent ~* '^aws-sqsd\/.*$') {
        set $redirect_to_https 0;
    }

    if ($redirect_to_https = 1) {
        return 301 https://$host$request_uri;
    }
}

This worked for the ELB health check (200), but the SQS worker is still failing, but now with a 404.

The only config that has working correctly on the SQS worker is a stripped down version (which I'm deploying manually right now specifically to the worker):

location / {
    try_files $uri $uri/ /index.php?$query_string;
    gzip_static on;
}

Here is a screenshot of the successful POST request using the above config:

enter image description here

So the question is, how do I set up my config file to ignore the HTTPS 301 redirect for both the ELB health check and the SQS queue without having to deploy separate NGINX config files?

  • 1
    Look [here](https://serverfault.com/questions/674900/nginx-if-statement-inside-location-returns-404/674901). "If" is evil and should be avoided, because it disrupt normal nginx behavior. Try [map](https://serverfault.com/questions/316541/check-several-user-agent-in-nginx) instead. – kab00m Oct 26 '20 at 16:45
  • How would I use that in my case? It seems like I would end up with if's regardless of where I check for the UA. – Samsquanch Oct 26 '20 at 17:19

2 Answers2

0

This is a classic instance of the "If is evil" NGINX scenario. There's a whole page dedicated to explaining how and why using if directives inside the location block can be problematic/give completely unexpected results.

I would recommend using NGINX map for UA routing. Or if you must use the if inside location, at least replace return 301 with rewrite as the latter is much more stable inside location blocks. Best of luck!

  • Thanks for the reply. I did previously have `rewrite ^ https://$host$request_uri? permanent;` instead of the 301, but various sites/articles said basically the opposite that you did about rewrite, so I changed it to the return. As for the `map` functionality, how would I use that in my case? It seems like I would end up with if's regardless of where I check for the UA. – Samsquanch Oct 26 '20 at 17:18
  • Why do you consider `rewrite` safe in this case? Nginx page tells that only 100% safe things are `return` and `rewrite ... last`. In this case, `rewrite ... last` is not an option since 301 redirect is desired. – Tero Kilkanen Oct 27 '20 at 22:57
0

One could use map as follows:

map $http_x_forwarded_proto $proto {
    default 1;
    "~^https$" 0;
}

map $http_user_agent $agent {
    default 1;
    "~*^ELB-HealthChecker\/.*$" 0;
    "~*^aws-sqsd\/.*$" 0;
}

The maps need to be defined in http level in configuration.

Then, in the location, one would have:

if ($proto$agent = "11") {
    return 301 https://$host$request_uri permanent;
}
Tero Kilkanen
  • 34,499
  • 3
  • 38
  • 58