43

Currently every invalid page is 500 (Internal Server Error) because I probably messed up with my server block configuration.

I decided to shut down my website a while ago and created a simple one-page, thank-you homepage. However old links and external sites are still trying to access other parts of the site, which no longer exists.

How do I force redirect all non-homepage (any invalid URL) to the homepage?

I tried with the following block, but it didn't work:

location / {
    try_files $uri $uri/ $document_uri/index.html;
}

My current configuration is (I don't even serve PHP files right now, ie homepage is simple html):

server {
    server_name www.example.com example.com;
    access_log /srv/www/example.com/logs/access.log;
    error_log /srv/www/example.com/logs/error.log;
    root /srv/www/example.com/public_html;
    index index.php index.html;

    location / {
        try_files $uri $uri/ $document_uri/index.html;
    }

    # Disable favicon.ico logging
    location = /favicon.ico {
        log_not_found off;
        access_log off;
    }

    # Allow robots and disable logging
    location = /robots.txt {
        allow all;
        log_not_found off;
        access_log off;
    }

    # Enable permalink structures
    if (!-e $request_filename) {
        rewrite . /index.php last;
    }

    # Handle php requests
    location ~ \.php$ {
        try_files $uri = 404;
        include /etc/nginx/fastcgi_params;
        fastcgi_pass  127.0.0.1:9000;
        fastcgi_index index.php;
        fastcgi_send_timeout 900;
        fastcgi_read_timeout 900;
        fastcgi_connect_timeout 900;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }

    # Disable static content logging and set cache time to max
    location ~* \.(jpg|jpeg|gif|png|css|js|ico|xml)$ {
        access_log off;
        log_not_found off;
        expires max;
    }

    # Deny access to htaccess and htpasswd files
    location ~ /\.ht {
        deny  all;
    }

    # Deny access to hidden files (beginning with a period)
    location ~ /\. {
        access_log off; log_not_found off; deny all;
    }
}
JJJ
  • 32,902
  • 20
  • 89
  • 102
hobbes3
  • 28,078
  • 24
  • 87
  • 116

9 Answers9

71

Setting the error page to the home page like this

error_page 404 /index.html;

has a small problem, the status code of the home page will be "404 not found", if you want to load the home page with a "200 ok" status code you should do it like this

error_page 404 =200 /index.html;

This will convert the "404 not found" error code to a "200 ok" code, and load the home page

The second method which @jvperrin mentioned is good too,

try_files $uri $uri/ /index.html;

but you need to keep 1 thing in mind, since it's the location / any asset that doesn't match another location and is not found will also load the index.html, for example missing images, css, js files, but in your case I can see you already have another location that's matching the assets' extensions, so you shouldn't face this problem.

Mohammad AbuShady
  • 40,884
  • 11
  • 78
  • 89
  • 3
    I can't think of a situation where you need to "override" the real http status code. If a page is not found, it should return a 404 status code. – ILikeTacos Feb 25 '14 at 20:44
  • I remember that we once did need to do that at work, but I can't recall why. – Mohammad AbuShady Feb 26 '14 at 06:54
  • 1
    @AlanChavez If you're running a shop, all visitors to your site should be "convert-able". If someone gets an old or broken URL, and sees the 404 page, he's worth $0. If you redirect him to the home page, there is the potential he'll look around. Sending a 200 status code all the time also ensures that tracking and analytics systems get as much data as possible -- many systems will see the 404 status and assume the page leads nowhere. – William Feb 03 '16 at 01:47
  • 1
    @William your bring up a good point, although as far as I know the biggest search engines frown upon "soft 404s" (pages that return a non-404|410 status code for 404 pages) because otherwise the bots will crawl and index a page that doesn't exist. I've never had an issue an analytics not tracking pages that return 404 codes, but of course I have not tried all the solutions out there. – ILikeTacos Feb 03 '16 at 06:36
  • i get this: nginx: [emerg] invalid value "=200" in /etc/nginx/sites-enabled/default:34 after adding the error_page option – tibi Aug 19 '16 at 13:28
  • i think you missed the ; at the end of the line – tibi Aug 19 '16 at 13:33
  • @MohammadAbuShady About "but you need to keep 1 thing in mind..." Do you refer to `error_page 404 =200 /index.html;` or second method? 404 to 200 method affects to files, scripts, etc too, absolute or relative paths. This is a problem because I need to apply this rule only to directories (URL paths without files) and I'm guessing how to do it. – ProtectedVoid Jan 31 '18 at 20:07
  • @ProtectedVoid I'm not sure I understand correctly, you could add location blocks and then add error page inside the location block, this way it will only be applied on locations that match that block. If I misunderstood you the please try to clarify the question/concern. – Mohammad AbuShady Feb 01 '18 at 06:08
  • 1
    @ILikeTacos actually it's a really common need. Single page web apps need to redirect routes back to the single page. – Clay Risser Jan 22 '20 at 10:14
40

To get a true redirect you could do this:

in server block define the error-page you want to redirect like this:

# define error page
error_page 404 = @myownredirect;
error_page 500 = @myownredirect;

Then you define that location:

# error page location redirect 302
location @myownredirect {
  return 302 /;
}

In this case errors 404 and 500 generates HTTP 302 (temporary redirect) to / (could of course be any URL)

If you use fast-cgi for php or other these blocks must have the following added to send the errors "upstrem" to server-block:

fastcgi_intercept_errors on;
030
  • 10,842
  • 12
  • 78
  • 123
6

This solution for nginx hosted site:

Edit your virtual hosting file

sudo nano /etc/nginx/sites-available/vishnuku.com 

Add this snippet in the server block

# define error page
error_page 404 = @notfound;

# error page location redirect 301
location @notfound {
    return 301 /;
}

In your php block put the fastcgi_intercept_errors set to on

location ~ \.php$ {
    include /etc/nginx/fastcgi_params;
    # intercept errors for 404 redirect
    fastcgi_intercept_errors on;
    fastcgi_pass unix:/run/php/php7.2-fpm.sock;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}

Final code will look like this

server {
    listen 80;
    server_name vishnuku.com;

    root /var/www/nginx/vishnuku.com;
    index index.php index.html;

    access_log /var/log/nginx/vishnuku.com.log;
    error_log /var/log/nginx/vishnuku.com.error.log;

    location / {
        try_files $uri $uri/ /index.php?$args /;
    }

    # define error page
    error_page 404 = @notfound;

    # error page location redirect 301
    location @notfound {
        return 301 /;
    }

    location ~ \.php$ {
        include /etc/nginx/fastcgi_params;
        fastcgi_intercept_errors on;
        fastcgi_pass unix:/run/php/php7.2-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }

    location ~ /\.ht {
        deny all;
    }

    location = /nginx.conf {
        deny all;
    }
}
Vishnu
  • 3,899
  • 2
  • 18
  • 19
5

Try adding the following line after your index definition:

error_page 404 /index.html;

If that doesn't work, try changing your try_files call to the following instead:

try_files $uri $uri/ /index.html;

Hopefully one of those works for you, I haven't tested either yet.

jvperrin
  • 3,368
  • 1
  • 23
  • 33
  • 1
    I think this would work except that I get 500 for any invalid page instead of 404. Can you see anything else wrong with my config? – hobbes3 Oct 21 '13 at 20:52
  • When I go to, say, `www.example.com/asdasasd/` (which doesn't exist), I get an error like `rewrite or internal redirection cycle while internally redirecting to "404"`. – hobbes3 Oct 22 '13 at 22:05
  • Both `error_page` and `try_files` won't work together, so try them individually instead of both together, since I think it redirects twice when they are used together. – jvperrin Oct 22 '13 at 23:01
  • It was something to do with the `location ~ \.php$` block since if I comment out that whole block, it works fine with the `error_page` and `try_files` together. Unfortunately, I don't really know *why* the php block is breaking this config. – hobbes3 Oct 23 '13 at 04:05
2

Must use

"fastcgi_intercept_errors on;"

along with the custom redirect location like error_page 404 =200 /index.html or

as above

location @myownredirect {
  return 302 /;
}
Anto
  • 3,128
  • 1
  • 20
  • 20
2

Try this:

error_page 404 $scheme://$host/index.html;
1

Hi first there are a lot of different ways to redirect all the 404 to the home page this helps you in SEO make such u use

 fastcgi_intercept_errors on;

than add this following to your config

        error_page 404 =301 http://yourdomain.com/;
    error_page 403 /error403.html;
    error_page 500 502 503 504 /50x.html;
    location = /50x.html {
            root /var/www/html;

    }
1

Came here when I was looking to implement similar where I'm using Nginx as a reverse proxy to a self-hosted MinIO bucket, and none of the above answers have this. So, if you're using proxy_pass and are intercepting errors, you can also handle this logic to redirect to the index.html page.

Handling URLs such as:

  • http://localhost/ - 200 OK
  • http://localhost/index.html - 200 OK
  • http://localhost/non-existant/ - 404 Not Found
server {
  listen 80;
  server_name localhost;

  location / {
    rewrite ^/$ /static/index.html break;
    proxy_intercept_errors on;
    proxy_pass http://something-to-proxy/static/;
  }

  error_page 404 /index.html;
}
Reece
  • 574
  • 3
  • 10
1

Another-correct way to redirect from error page to the home page. The setup example applies to the nginx server configuration file:

...
http {
    ...
    server{
        ...
        #Catch 40x errors: 
        error_page 400 401 402 403 404 = @RedirectToHome;

        #Catch 50x errors: 
        error_page 500 501 502 503 504 = @RedirectToHome;

        #We are now redirecting to the homepage of the site
        location @RedirectToHome {
            return 301 http://www.example.com;
            #return 301 https://www.example.com;
            #return 301 /;
        }
        ...
    }
}

You should avoid setting return 301 /; if port forwarding to the server was performed somewhere, because this nginx redirection will then be performed to the port on which the server is listening for incoming connections. Therefore, it is better to set the correct hostname(site name) in this configuration.