Opencart 语言前缀导致 Nginx 404

Opencart language prefix causes Nginx 404

环境:nginx + php-fpm

我已经安装了OC 2.1

当我在网站周围使用 SEO URL 时没有问题。
example.com/news - [OK, 200]
示例。com/de/news - [好的,200]

但有时我没有 SEO URL
example.com/index.php?route=information/testimonials&testimonial_id=6 - [OK, 200]
例如。com/en/index.php?route=information/testimonials&testimonial_id=6 - [错误, 404]

NGINX:

server {
  server_name example.com;

  root /var/www/example.com;
  index index.php index.html index.htm;
  charset UTF-8;
  autoindex off;

  # Show "Not Found" 404 errors in place of "Forbidden" 403 errors, because
  # forbidden errors allow attackers potential insight into your server's
  # layout and contents
  error_page 403 =404;

  # It's always good to set logs, note however you cannot turn off the error log
  # Setting error_log off; will simply create a file called 'off'
  access_log /var/log/nginx/example.com.access.log;
  error_log /var/log/nginx/example.com.error.log;

  # SEO URL Settings
  # Nginx configuration of OC htaccess

  rewrite ^/sitemap.xml$ /index.php?route=feed/sitemap last;
  rewrite ^/sitemap([^\.]+).xml$ /index.php?route=feed/sitemap&path= last;

  location = /googlebase.xml {
    rewrite ^(.*)$ /index.php?route=feed/google_base; 
  } 

  location / {
    # This try_files directive is used to enable SEO-friendly URLs for OpenCart
    try_files $uri $uri/ @opencart;
  }

  location @opencart {
    rewrite ^([^?]*) /index.php?_route_= last;
  }

  # End SEO settings

  # Make sure files with the following extensions do not get loaded by nginx because nginx would display the source code, and these files can contain PASSWORDS!
  location ~* \.(engine|inc|info|ini|install|log|make|module|profile|test|po|sh|.*sql|theme|tpl(\.php)?|xtmpl)$|^(\..*|Entries.*|Repository|Root|Tag|Template)$|\.php_ {
    deny all;
  }

  # Do not log access to the favicon, to keep the logs cleaner
  location = /favicon.ico {
    log_not_found off;
    access_log off;
  }

  location = /apple-touch-icon.png {
    log_not_found off;
    access_log off;
  }

  location = /apple-touch-icon-precomposed.png {
    log_not_found off;
    access_log off;
  }

  # This block will catch static file requests, such as images, css, js
  # The ?: prefix is a 'non-capturing' mark, meaning we do not require
  # the pattern to be captured into  which should help improve performance
  location ~* \.(?:3gp|gif|jpg|jpe?g|png|ico|wmv|avi|asf|asx|mpg|mpeg|mp4|pls|mp3|mid|wav|swf|flv|txt|js|css|exe|zip|tar|rar|gz|tgz|bz2|uha|7z|doc|docx|xls|xlsx|pdf|iso|woff|woff2|eot|otf|ttf)$ {
    # Some basic cache-control for static files to be sent to the browser
    expires max;
    add_header Pragma public;
    add_header Cache-Control "public, must-revalidate, proxy-revalidate";
  }

  # Deny all attempts to access hidden files such as .htaccess, .htpasswd, .DS_Store (Mac).
  # Keep logging the requests to parse later (or to pass to firewall utilities such as fail2ban)
  location ~ /\. {
    access_log off;
    log_not_found off;
    deny all;
  }

  location ~ ~$ {
    access_log off;
    log_not_found off;
    deny all;
  }

  # Deny access to any files with a .php extension in these directories
  # Works in sub-directory installs and also in multisite network
  # Keep logging the requests to parse later (or to pass to firewall utilities such as fail2ban)
  location ~* /(?:cache|logs|image|download)/.*\.php$ {
    deny all;
  }
  
  # Make sure these get through
  location = /robots.txt {
    allow all;
    log_not_found off;
    access_log off;
  }
  
  # Fix for Firefox issue with cross site font icons
  location ~* \.(eot|otf|ttf|woff)$ {
    add_header Access-Control-Allow-Origin *;
  }

  # redirect server error pages to the static page /50x.html
  error_page 500 502 503 504 /50x.html;
    location = /50x.html {
    root /usr/share/nginx/www;
  }

  # Pass all .php files onto a php-fpm/php-fcgi server.
  location ~ [^/]\.php(/|$) {
    # Regex to split $uri to $fastcgi_script_name and $fastcgi_path
    fastcgi_split_path_info ^(.+\.php)(/.+)$;

    # Check that the PHP script exists before passing it
    try_files $fastcgi_script_name =404;

    # Bypass the fact that try_files resets $fastcgi_path_info
    # see: http://trac.nginx.org/nginx/ticket/321
    set $path_info $fastcgi_path_info;
    fastcgi_param PATH_INFO $path_info;

    fastcgi_pass unix:/run/php/php5.6-fpm.sock;
    fastcgi_index index.php;
    # Uncomment if site is HTTPS
    fastcgi_param HTTPS on;
    include fastcgi.conf;
  }

    listen [::]:443 ssl ipv6only=on; # managed by Certbot
    listen 443 ssl; # managed by Certbot
    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

} # End of server block.


server {
    if ($host = example.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot

    listen 80;
    listen [::]:80;
    server_name example.com;
    return 404; # managed by Certbot
}

但在 ENV 中:php + apache2 一切正常,运行良好:)

Options +FollowSymlinks

Options -Indexes

<FilesMatch "(?i)((\.tpl|\.ini|\.log|(?<!robots)\.txt))">
 Order deny,allow
 Deny from all
</FilesMatch>

RewriteEngine On

#RewriteCond %{HTTP_HOST} ^www.dev.example.com
#RewriteRule ^(.*)$ http://dev.example.com/ [R=301,L]

RewriteCond %{THE_REQUEST} ^[A-Z]{3,9}\ /index\ HTTP/
RewriteRule ^index\.html$ / [R=301,L]
RewriteCond %{THE_REQUEST} ^[A-Z]{3,9}\ /index\.php\ HTTP/
RewriteRule ^index\.php$ / [R=301,L]

RewriteBase /
RewriteRule ^sitemap.xml$ index.php?route=feed/sitemap [L]
RewriteRule ^sitemap([^\.]+).xml$ index.php?route=feed/sitemap&path= [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !.*\.(ico|gif|jpg|jpeg|png|js|css)
RewriteRule ^([^?]*) index.php?_route_= [L,QSA]

php_value max_input_vars 10000
php_value suhosin.post.max_vars 10000
php_value suhosin.request.max_vars 10000

我猜 nginx 规则和指令有些问题,它尝试打开 /de/ 目录并导致 404 错误页面,但 apache2 工作正常并且不会尝试打开 /de/ 目录。
我错过了什么?

这是您的 nginx 配置发生的情况。当一些一般请求(比如 /de/news)到达您的服务器时,它由 location / { ... } 块处理,其中 try_files 指令首先检查 /var/www/example.com/de/news 文件存在,然后是您定义的索引文件存在于 /var/www/example.com/de/news 目录中,然后使用 @opencart 命名位置继续请求处理,您的请求在此处转换为 /index.php?_route_=/de/news。之后 rewrite nginx 指令的 last 标志强制 nginx 为这个转换后的请求搜索一个新位置,即 location ~ [^/]\.php(/|$) { ... } 一个。但是当你收到像 /en/index.php?route=information/testimonials&testimonial_id=6 这样的请求时,它会绕过默认的 location / { ... } 直接转到该位置,正则表达式匹配位置优先于前缀位置。在 fastcgi_split_path_info 指令之后,您的 $fastcgi_script_name 变量等于 /en/index.php 并且 try_files $fastcgi_script_name =404; 显然会给您一个 HTTP 404 错误。

你可以尝试做什么?例如,您可以尝试 try_files $fastcgi_script_name /index.php?_route_=$uri&$args;。该指令会将您的 /en/index.php?route=information/testimonials&testimonial_id=6 请求转换为 /index.php?_route_=/en/index.php&route=information/testimonials&testimonial_id=6。您可以重写您的 URI,将此语言前缀移动到某个查询变量,例如

location ~ [^/]\.php(/|$) {
    if ($uri ~ ^/(?<lang>en|de|fr)(?<path>/.*)) {
        set $args $args&language=$lang;
        rewrite ^ $path break;
    }
    ...
}

你可以做任何你想做的事,总有几种方法可以解决与 IT 相关的任务。我希望您了解一般情况。

后记

而不是

  location / {
    # This try_files directive is used to enable SEO-friendly URLs for OpenCart
    try_files $uri $uri/ @opencart;
  }

  location @opencart {
    rewrite ^([^?]*) /index.php?_route_= last;
  }

你可以只使用

  location / {
    # This try_files directive is used to enable SEO-friendly URLs for OpenCart
    try_files $uri $uri/ /index.php?_route_=$uri&$args;
  }

通过 rewrite ^([^?]*) /index.php?_route_= last; 你可能意味着在没有查询字符串的情况下获取 URI 部分,但是 rewrite 指令(以及 location 指令)与规范化的 URI 一起工作无论如何不包括查询字符串。如果您想保留两个 location 块,可以安全地将此规则更改为 rewrite ^ /index.php?_route_=$uri last;.