nginx static index redirect - redirect

This seems ridiculous but I've not found a working answer in over an hour of searching.
I have a static website running off nginx (which happens to be behind Varnish). The index file is called index.html. I want to redirect anyone who actually visits the URL mydomain.com/index.html back to mydomain.com.
Here is my nginx config for the site:
server {
listen 8080;
server_name www.mydomain.com;
port_in_redirect off;
location / {
root /usr/share/nginx/www.mydomain.com/public;
index index.html;
}
rewrite /index.html http://www.mydomain.com/ permanent;
}
http://www.mydomain.com/index.html responds as expected with a 301 with the location http://www.mydomain.com/ but unfortunately http://www.mydomain.com/ also serves a 301 back to itself so we get a redirect loop.
How can I tell nginx to only serve the 301 if index.html is literally in the request?

Add a new location block to handle your homepage, and use try_files directive (instead of "index index.html;") to look for the index.html file directly. Note that try_files requires you to enter at least 2 choices. So I put the same file twice.
location = / {
root /usr/share/nginx/www.mydomain.com/public;
try_files /index.html /index.html;
}
Looks good based on my experiment:
curl -iL http://www.mydomain.com/index.html
HTTP/1.1 301 Moved Permanently
Server: nginx
Date: Sat, 16 Mar 2013 09:07:27 GMT
Content-Type: text/html
Content-Length: 178
Connection: keep-alive
Location: http://www.mydomain.com/
HTTP/1.1 200 OK
Server: nginx
Date: Sat, 16 Mar 2013 09:07:27 GMT
Content-Type: text/html
Content-Length: 4
Last-Modified: Sat, 16 Mar 2013 08:05:47 GMT
Connection: keep-alive
Accept-Ranges: bytes
[UPDATE]
The root cause of the redirect loop is the 'index' directive, which triggers nginx to do another round of location match again. That's how the rewrite rule outside the location block gets executed again, causing the loop. So the 'index' directive is like a "rewrite...last;" directive. You don't want that in your case.
The trick is to not trigger another location match again. try_files can do that efficiently. That's why I picked it in my original answer. However, if you like, another simple fix is to replace
index index.html;
by
rewrite ^/$ /index.html break;
inside your original "location /" block. This 'rewrite...break;' directive will keep nginx stay inside the same location block, effectively stop the loop. However, the side effect of this approach is that you lose the functionality provided by 'index' directive.
[UPDATE 2]
Actually, index directive executes after rewrite directive. So the following also works. Note that I just added the rewrite...break; line. If the request uri is "/", nginx finds the existing file /index.html from the rewrite rule first. So the index directive is never being triggered for this request. As a result, both directives can work together.
location / {
root /usr/share/nginx/www.mydomain.com/public;
index index.html;
rewrite ^/$ /index.html break;
}

Looks like you really don't want index.php to show up in the address bar, is that correct?
If you add a rewrite directive to the nginx config, you'll get a redirect loop, as you have experienced. If you are open to a javascript solution, you can place this anywhere in your index.html to silently rewrite the address bar:
<script>
history.pushState(null, '', '/');
</script>
For more information
Keep in mind that while most modern browsers support the history API, not all do (namely, most versions of IE).

Related

404 redirect to another server/domain

I'm looking for a solution with redirects to another domain if the response from HTTP server was 404.
acl not_found status 404
acl found_ceph status 200
use_backend minio_s3 rsprep ^HTTP/1.1\ 404\ (.*)$ HTTP/1.1\ 302\ Found\nLocation:\ / if not_found
use_backend ceph if found_ceph
But still not working, this rule goes to minio_s3 backend.
Thank you for you advice.
When the response from this backend has status 404, first add a Location header that will send the browser to example.com with the original URI intact, then set the status code to 302 so the browser executes a redirect.
backend my-backend
mode http
server my-server 203.0.113.113:80 check inter 60000 rise 1 fall 2
http-response set-header Location http://example.com%[capture.req.uri] if { status eq 404 }
http-response set-status 302 if { status eq 404 }
Test:
$ curl -v http://example.org/pics/funny/cat.jpg
* Hostname was NOT found in DNS cache
* Trying 127.0.0.1...
* Connected to example.org (127.0.0.1) port 80 (#0)
> GET /pics/funny/cat.jpg HTTP/1.1
> User-Agent: curl/7.35.0
> Host: example.org
> Accept: */*
The actual back-end returns 404, but we don't see it. Instead...
< HTTP/1.1 302 Moved Temporarily
< Last-Modified: Thu, 04 Aug 2016 16:59:51 GMT
< Content-Type: text/html
< Content-Length: 332
< Date: Sat, 07 Oct 2017 00:03:22 GMT
< Location: http://example.com/pics/funny/cat.jpg
The response body from the back-end's 404 error page will still be sent to the browser, but -- as it turns out -- the browser will not display it, so no harm done. This requires HAProxy 1.6 or later.
#Michael's answer is rather good, but isno't working for me for two reasons:
Mainly because the %[capture.req.uri] tag resolves to empty (HA Proxy 1.7.9 Docker image)
Also due to the fact that the original assumptions are incomplete, due to the fact that the frontend section is missing...
So I struggled for a while, as you find all kinds of answers on the Internet, between those guys who swear the 404 logic should be put in the frontend, vs those who choose the backend, and any possible kind of tags...
This is my answer, which works for me.
My use case is that if an image is not found on the backend behind HA Proxy, then an S3 bucket is checked.
The entry point is: https://myhostname:8080/path/to/image.jpeg
defaults
mode http
global
log 127.0.0.1:514 local0 debug
frontend come_on_over_here
bind :8080
# The following two lines are here to save values while we have access to them. They won't be available in the backend section.
http-request set-var(txn.path) path
http-request set-var(txn.query) query
http-request replace-value Host localhost:8080 dev.local:80
default_backend onprems_or_s3_be
backend onprems_or_s3_be
log global
acl path_photos var(txn.path) -m beg /path/prefix/i/want/to/strip/off
acl p_ext_jpeg var(txn.path) -m end .jpeg
acl is404 status eq 404
http-response set-header Location https://mybucket.s3.eu-west-3.amazonaws.com"%[var(txn.path),regsub(^/path_prefix_i_want_to_strip_off/,/)]?%[var(txn.query)]" if path_photos p_ext_jpeg is404
http-response set-status 301 if is404
server onprems_server dev.local:80 check

NGINX redirects to http

I am a newbie to NGINX and have been trying to get this problem sorted out.
Here is the NGINX configuration that works pretty well for most of the part, however when a request is placed without the trailing slash at the end, it redirects to http://$host instead of https://$host. It was forwarding with the port earlier, but I turned off port_in_redirect, which disabled showing up the port number in the browser.
Somehow https://domain.com/xyz/abc still gets redirected to http://domain.com/xyz/abc/
My guess is try_files is somehow not retaining the domain name
I am sure there is something wrong in the configuration, but I have no deep insights on whats causing it
Any inputs is highly appreciate
server {
listen 8080;
server_name _;
location /xyz/abc {
alias /var/www/html/;
try_files $uri $uri/ /xyz/abc/index.html;
}
location ~ ^/xyz/foo/(.*) {
return 301 https://$host/xyz/abc/foo/$1;
}
Below is the curl output
curl -I https://domain.com/xyz/abc
HTTP/1.1 301 Moved Permanently
Server: nginx/1.6.3
Date: Thu, 27 Jan 2016 08:18:27 GMT
Content-Type: text/html
Content-Length: 184
Location: http://domain.com/xyz/abc/
Connection: keep-alive

Right way to enable requests with method DELETE in nginx

I was writing a RESTful applicaiton in PHP and enabled DELETE, PUT requests for nginx.
location / {
root html;
index index.php index.html index.htm;
dav_methods PUT DELETE;
}
When I executed a REST Request with method DELETE, which I wanted to handle inside my index.php - nginx removed the html folder.
What is the right way to tell nginx to pass DELETE requests to my index.php ?
Nginx does not disable PUT or DELETE requests, but it does not allow these requests on a folder index. There isn't really anything that needs to be enabled with nginx (you should remove the dav_methods line), but you need to avoid accessing your index.php through the index directive like:
index index.php index.html index.htm;
Instead use try_files to match the index.php file ie like:
try_files $uri /index.php$is_args$args;
In this case nginx wont complain about your DELETE method.
NginX executes those HTTP methods (DELETE, PUT) directly without even invoking PHP engine because they are handled by the DAV extension inside nginX.
To overcome the issue, you may use POST HTTP method for all of your API calls, but add additional custom header to indicate the actual REST method - instead of this
PUT /api/Person/4 HTTP/1.1
Host: localhost:10320
Content-Type: application/json
Cache-Control: no-cache
you will invoke this
POST /api/Person/4 HTTP/1.1
Host: localhost:10320
Content-Type: application/json
X-REST-Method: PUT
Cache-Control: no-cache
and then in PHP you will check in this way
if($_SERVER['HTTP_X_REST_METHOD']!='')
switch($_SERVER['HTTP_X_REST_METHOD'])
{
case 'PUT':
...
break;
case 'PATCH':
...
break;
case 'DELETE':
...
break;
}

Redirect nginx config server_name to custom 404 error page

I'm new to nginx configs and have spent a lot of time googling so far. I'm trying to create a very basic nginx config file to be used in a "redirect" server.
Users will be required to point naked domains (example.com) by A-record to my redirect server IP address, and the 'www' record by CNAME to another server.
The purpose of the redirect server is to then perform a 301 redirect any/wildcard naked domains back to to the 'www' version of the domain so it can be properly handled by my other server.
But I also want to catch any misconfigured 'www' domains that are pointing to my server IP by A-record, and simply direct them to a custom error page on the redirect server with further instructions on how to set up their account correctly for my service.
Here's what I have. It works, but since I am new to writing configs I was wondering if there is a better way to handle the redirect to the custom error page in the first server block. TIA!
#redirect to error page if begins with 'www.'
server {
listen 80;
server_name ~^www.; #only matches if starts with 'www.'. Is this good enough?
rewrite ^(.*)$ /404.html; #is this the correct way to direct to a custom error page?
error_page 404 /404.html;
location = /404.html {
root /usr/share/nginx/html;
}
}
#no match, so redirect to www.example.com
server {
listen 80 default_server;
rewrite ^(.*)$ $scheme://www.$host$1 permanent;
}
Prefix/suffix server name matching is faster and easier than regexp.
Also, there is no reason to use rewrite. You want to return 404, so do it and nginx will do all the rest. BTW, with rewrite you will return 200 OK with content of /404.html instead of 404 Not Found.
So here it is:
server {
listen 80;
server_name www.*;
root /usr/share/nginx/html;
error_page 404 /404.html;
location / {
return 404;
}
location = /404.html {
internal;
}
}

Nginx redirect to an external URL

What I'm trying to do is route all requests to /rdr/extern_url to redirect to extern_url through my web server instead of doing it through PHP.
location /rdr {
rewrite ^/rdr/(.*)$ $1 permanent;
}
What's wrong here is it if I access http://localhost/rdr/http://google.com my browser is telling me:
Error 310 (net::ERR_TOO_MANY_REDIRECTS): There were too many redirects.
How do I redirect properly?
Trivial check:
$ curl -si 'http://localhost/rdr/http://www.google.com' | head -8
HTTP/1.1 301 Moved Permanently
Server: nginx/1.2.0
Date: Sun, 05 Aug 2012 09:33:14 GMT
Content-Type: text/html
Content-Length: 184
Connection: keep-alive
Location: http:/www.google.com
As you can see, there is only one slash after scheme in Location.
After adding the following directive to server:
merge_slashes off;
We'll get the correct reply:
$ curl -si 'http://localhost/rdr/http://www.google.com' | head -8
HTTP/1.1 301 Moved Permanently
Server: nginx/1.2.0
Date: Sun, 05 Aug 2012 09:36:56 GMT
Content-Type: text/html
Content-Length: 184
Connection: keep-alive
Location: http://www.google.com
It becomes clear from the comments you may want to pass hostname without the schema to your redirecting service. To solve this problem you need to define two locations to process both cases separately:
server {
listen 80;
server_name localhost;
merge_slashes off;
location /rdr {
location /rdr/http:// {
rewrite ^/rdr/(.*)$ $1 permanent;
}
rewrite ^/rdr/(.*)$ http://$1 permanent;
}
}
Here I've defined /rdr/http:// as a sub-location of /rdr just to keep the redirector service in one block -- it's perfectly valid to create both locations at server-level.