I have the following scenario: a nginx serving static files to clients. For a request, the file is searched for in a local directory and, if not found, the request is forwarded to Amazon S3 via a proxy_pass directive.

The problem is that Amazon S3 returns HTTP 403 if a file is not found and I would like to change that code to 404 for the client.

(As a side note, this machine running nginx is authorized to retrieve the files from S3 without any credentials and that works properly)

My config looks like this:

user nobody;
worker_processes auto;
pid /run/nginx.pid;
events {
    worker_connections 2048;
    multi_accept on;
    use epoll;

http {
    reset_timedout_connection on;
    types_hash_max_size 2048;

    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    gzip off;
    open_file_cache off;

    server {
        listen 8000 backlog=1000;
        server_name blablabla;

        # Requests made to this server are in the form of GET /file/<sha256> and 
        # for aabbc1111111111...11 the file will be located in:
        # * /files/aa/bb/c/aabbc111111...11.bin on the disk
        # * s3://my-bucket-name/aa/bb/c/aabbc1111111...11.bin in S3
        location ~ "^/file/(?<a>[0-9a-fA-F]{2})(?<b>[0-9a-fA-F]{2})(?<c>[0-9a-fA-F])(?<d>[0-9a-fA-F]{59})$" {
            root /files;
            rewrite ^ /$a/$b/$c/$a$b$c$d.bin break;

            # If 404 results from trying to serve the file from the local directory,
            # move to S3
            error_page 404 = @try_s3;

        location @try_s3 {
            rewrite ^(.*)\.bin$ /my-bucket-name$1 break;
            return 404;

            proxy_http_version 1.1;
            proxy_pass https://s3-eu-west-1.amazonaws.com;
            proxy_set_header Connection ""; break;
            proxy_intercept_errors on;

            # This is not working, I still get 403, but the difference is that 
            # with it, I get 403 from nginx (with its 403 HTML template), without
            # it I get the 403 sent from S3 (with their HTML in it)
            error_page 403 =404;

        # This is used for the load balancer
        location /ping {
            return 200;

        location / {
            return 404;

1 Answer 1


The error_page directive is what you want to use here, so you're on the right track. The documentation indicates that the uri parameter isn't optional. You might be getting tripped up by that. Try something like this:

error_page 403 =404 /404.html;

Making sure to create a 404.html somewhere useful.

You might also be able to "trick" nginx into giving you back a 404 by pointing the 403 error page to a file that doesn't exist:

error_page 403 /this-does-not-exist.flibettyjibbety;

This may work, because nginx returns the last error code encountered during URI processing.

If all this doesn't work, then you'll want to get some debug logging of the request being processed, and if you can't figure it out, edit it into your question for further analysis.

  • 2
    I could have sworn I tried it with error_page 403 =404 @return_404; and a custom @return_404 location and it didn't work, but now it seems it does. Must have been some other issue... Anyway, using error_page 403 =404 /; worked. Thanks!
    – Unknown
    Sep 16, 2015 at 8:13

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .