Почему в Nginx страница ошибки 405 может отображаться правильно в основном блоке сервера, но не в блоках сервера, которые перенаправляют на основной блок?

Почему в Nginx страница ошибки 405 может отображаться правильно в основном блоке сервера, но не в блоках сервера, которые перенаправляют на основной блок?

Я использую два блока сервера для пересылки всех запросов в главный блок, который обслуживает все запросы с https://wwwпрефиксом. Когда я выдаю ошибку 405 в https://wwwзапросе, соответствующая страница ошибки отображается, как и ожидалось.

Однако, когда я использую префикс httpor httpsonly и использую Postman для отправки запроса DELETE или PATCH, запрос проходит через один из первых 2 серверных блоков, и ошибка не возвращается. Страница отображается нормально, как если бы был сделан запрос GET.

Как изменить конфигурацию, чтобы ошибки отображались независимо от префикса запроса?

user  nginx;
worker_processes  auto;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}


http {

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

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    # access_log  /var/log/nginx/access.log  main;
    access_log off;

    limit_req_zone $binary_remote_addr zone=mylimit:1m rate=50r/s;
    limit_req zone=mylimit burst=20 nodelay;

    sendfile      on;
    tcp_nopush    on;
    sendfile_max_chunk 1m;

    gzip on;
    gzip_comp_level 3;
    gzip_types text/css application/javascript application/x-javascript text/javascript;
    gzip_vary on;

    server_tokens off;
    resolver 8.8.8.8 8.8.4.4 [2001:4860:4860::8888] [2001:4860:4860::8844];

    error_page 400 /html/400.html;
    error_page 403 /html/403.html;
    error_page 404 /html/404.html;
    error_page 405 /html/405.html;
    error_page 500 502 503 504 /html/50x.html;

    server {

        listen 80;
        listen [::]:80;

        server_name example.com www.example.com;

        if ($request_method !~ ^(GET|HEAD|POST)$) {
            return 405;
        }

        return 301 https://www.example.com$request_uri;

    }

    server {

        listen 443 ssl http2;
        listen [::]:443 ssl http2;

        server_name example.com;

        ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; # managed by Certbot
        ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # managed by Certbot
        include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
        ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

        if ($request_method !~ ^(GET|HEAD|POST)$) {
            return 405;
        }

        return 301 https://www.example.com$request_uri;

    }


    server {

        listen 443 ssl http2;
        listen [::]:443 ssl http2;

        server_name www.example.com;

        ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; # managed by Certbot
        ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # managed by Certbot
        include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
        ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

        if ($request_method !~ ^(GET|HEAD|POST)$) {
            return 405;
        }

        ssl_stapling on;
        ssl_stapling_verify on;
        ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;

        root /srv/example/views/public;

        location ~* \.(jpg|png|svg|webp|ico)$ {
            valid_referers none blocked server_names ~\.bing\. ~\.duckduckgo\. ~\.facebook\. ~\.google\. ~\.instagram\. ~\.twitter\. ~\.yahoo\.;
            if ($invalid_referer) {
                return 403;
            }
            add_header content-security-policy "default-src 'self';";
            add_header cache-control "public, max-age=31536000";
            add_header x-content-type-options nosniff;
        }

        location ~* \.(css)$ {
            add_header content-security-policy "default-src 'self'; font-src 'self' https://fonts.gstatic.com fonts.googleapis.com; style-src 'self' fonts.googleapis.com;";
            add_header cache-control "public, max-age=2629746";
            add_header x-content-type-options nosniff;
        }

        location ~* \.(htm|html)$ {
            add_header content-security-policy "default-src 'self'; font-src 'self' https://fonts.gstatic.com fonts.googleapis.com; img-src 'self' https://www.youtube.com; media-src 'self' https://www.youtube.com; object-src 'none'; script-src 'self' https://www.google-analytics.com https://apis.google.com https://js.stripe.com; style-src 'self' fonts.googleapis.com;";
            add_header cache-control "public, max-age=2629746";
            add_header feature-policy "autoplay 'none'; legacy-image-formats 'none'; oversized-images 'none'; unsized-media 'none';";
            add_header permissions-policy "autoplay=(); legacy-image-formats=(); oversized-images=(); unsized-media=();";
            add_header referrer-policy strict-origin;
            add_header strict-transport-security "max-age=31557600; includesubdomains";
            add_header x-content-type-options nosniff;
            add_header x-frame-options sameorigin;
        }

        location ~* \.(js)$ {
            add_header content-security-policy "default-src 'self';";
            add_header cache-control "public, max-age=2629746";
            add_header x-content-type-options nosniff;
        }

        location / {
            add_header content-security-policy "default-src 'self'; font-src 'self' https://fonts.gstatic.com fonts.googleapis.com; img-src 'self' https://www.youtube.com; media-src 'self' https://www.youtube.com; object-src 'none'; script-src 'self' https://www.google-analytics.com https://apis.google.com https://js.stripe.com; style-src 'self' fonts.googleapis.com;";
            add_header cache-control "public, max-age=2629746";
            add_header feature-policy "autoplay 'none'; legacy-image-formats 'none'; oversized-images 'none'; unsized-media 'none';";
            add_header permissions-policy "autoplay=(); legacy-image-formats=(); oversized-images=(); unsized-media=();";
            add_header referrer-policy strict-origin;
            add_header strict-transport-security "max-age=31557600; includesubdomains";
            add_header x-content-type-options nosniff;
            add_header x-frame-options sameorigin;
            proxy_hide_header x-powered-by;
            proxy_pass http://127.0.0.1:8080;
        }

    }

    server {

        listen 80;
        listen [::]:80;

        server_name testbed.example.com;

        if ($request_method !~ ^(GET|HEAD|POST)$) {
            return 405;
        }

        return 301 https://testbed.example.com$request_uri;

    }

    server {

        listen 443 ssl http2;
        listen [::]:443 ssl http2;

        server_name testbed.example.com;

        ssl_certificate /etc/letsencrypt/live/testbed.example.com/fullchain.pem; # managed by Certbot
        ssl_certificate_key /etc/letsencrypt/live/testbed.example.com/privkey.pem; # managed by Certbot
        include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
        ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

        if ($request_method !~ ^(GET|HEAD|POST)$) {
            return 405;
        }

        ssl_stapling on;
        ssl_stapling_verify on;
        ssl_trusted_certificate /etc/letsencrypt/live/testbed.example.com/chain.pem;

        root /srv/testbed/views/public;

        location ~* \.(jpg|png|svg|webp|ico)$ {
            valid_referers none blocked server_names ~\.bing\. ~\.duckduckgo\. ~\.facebook\. ~\.google\. ~\.instagram\. ~\.twitter\. ~\.yahoo\.;
            if ($invalid_referer) {
                return 403;
            }
            add_header content-security-policy "default-src 'self';";
            add_header cache-control "public, max-age=31536000";
            add_header x-content-type-options nosniff;
        }

        location ~* \.(css)$ {
            add_header content-security-policy "default-src 'self'; font-src 'self' https://fonts.gstatic.com fonts.googleapis.com; style-src 'self' fonts.googleapis.com;";
            add_header cache-control "public, max-age=2629746";
            add_header x-content-type-options nosniff;
        }

        location ~* \.(htm|html)$ {
            add_header content-security-policy "default-src 'self'; font-src 'self' https://fonts.gstatic.com fonts.googleapis.com; img-src 'self' https://www.youtube.com; media-src 'self' https://www.youtube.com; object-src 'none'; script-src 'self' https://www.google-analytics.com https://apis.google.com https://js.stripe.com; style-src 'self' fonts.googleapis.com;";
            add_header cache-control "public, max-age=2629746";
            add_header feature-policy "autoplay 'none'; legacy-image-formats 'none'; oversized-images 'none'; unsized-media 'none';";
            add_header permissions-policy "autoplay=(); legacy-image-formats=(); oversized-images=(); unsized-media=();";
            add_header referrer-policy strict-origin;
            add_header strict-transport-security "max-age=31557600; includesubdomains";
            add_header x-content-type-options nosniff;
            add_header x-frame-options sameorigin;
        }

        location ~* \.(js)$ {
            add_header content-security-policy "default-src 'self';";
            add_header cache-control "public, max-age=2629746";
            add_header x-content-type-options nosniff;
        }

        location / {
            add_header content-security-policy "default-src 'self'; font-src 'self' https://fonts.gstatic.com fonts.googleapis.com; img-src 'self' https://www.youtube.com; media-src 'self' https://www.youtube.com; object-src 'none'; script-src 'self' https://www.google-analytics.com https://apis.google.com https://js.stripe.com; style-src 'self' fonts.googleapis.com;";
            add_header cache-control "public, max-age=2629746";
            add_header feature-policy "autoplay 'none'; legacy-image-formats 'none'; oversized-images 'none'; unsized-media 'none';";
            add_header permissions-policy "autoplay=(); legacy-image-formats=(); oversized-images=(); unsized-media=();";
            add_header referrer-policy strict-origin;
            add_header strict-transport-security "max-age=31557600; includesubdomains";
            add_header x-content-type-options nosniff;
            add_header x-frame-options sameorigin;
            proxy_hide_header x-powered-by;
            proxy_pass http://127.0.0.1:10001;
        }

    }

}

решение1

nginxдокументациясообщает следующее:

Указанное условие оценивается. Если true, то выполняются директивы этого модуля, указанные внутри фигурных скобок, и запросу назначается конфигурация внутри директивы if. Конфигурации внутри директив if наследуются от предыдущего уровня конфигурации.

В вашем примере конфигурации это будет означать, что return 301конфигурация унаследована от родительского уровня конфигурации. В документации не говорится, что происходит, когда предыдущий уровень конфигурации и текущий уровень содержат returnдирективу. Но из вашего результата следует, что унаследованная директива сохраняется.

Уже было предложено, что перенаправление будет включено в местоположение. Я пишу здесь точную конфигурацию, чтобы подчеркнуть, как должен выглядеть блок конфигурации:

server {
    listen 80;
    listen [::]:80;

    server_name example.com www.example.com;

    if ($request_method !~ ^(GET|HEAD|POST)$) {
        return 405;
    }

    location / {
        return 301 https://www.example.com$request_uri;
    }
}

Другая возможность — использовать limit_except. Это непроверенная версия:

server {
    listen 80;
    listen [::]:80;

    server_name example.com www.example.com;

    location /
        error_page 403 =405 /html/405.html;
        limit_except GET POST {
            deny all;
        }

        return 301 https://www.example.com$request_uri;
    }
}

limit_exceptразрешает методы GET и POST, все остальные запрещены. HEAD разрешен неявно, разрешая GET.

error_pageДиректива используется для преобразования 403кода ошибки в deny allкод 405возврата и отображения страницы ошибки.

Связанный контент