Apache و Nginx

کانفیگ Nginx برای وردپرس و رفع 404 پیوندهای یکتا

کانفیگ Nginx برای وردپرس یعنی یک server block که درخواست ها را با try_files به index.php می رساند و فایل های PHP را به PHP-FPM می فرستد. نبود همین try_files دلیل اصلی 404 پیوندهای یکتا بعد از انتقال سایت از Apache به Nginx است.

نوشته تیم فنی پیکوهاستبه روزرسانی: ۱۷ مهر ۱۴۰۵۸ دقیقه مطالعه

اگر وردپرس را روی Nginx برده اید و فقط صفحه اصلی باز می شود، ولی نوشته ها و برگه ها خطای 404 می دهند، مشکل تقریبا همیشه در کانفیگ Nginx برای وردپرس است. Nginx فایل .htaccess را نمی خواند، پس قانون بازنویسی آدرس ها که در Apache خودکار کار می کرد، باید به صورت دستور try_files در server block نوشته شود. در این راهنما یک کانفیگ کامل و آماده، بررسی خطاهای رایج و روش امن اعمال تغییر را می بینید.

چرا پیوندهای یکتا در Nginx 404 می دهند

در Apache وردپرس در .htaccess قانونی می نویسد که هر آدرسی را که فایل یا پوشه واقعی ندارد، به index.php بفرستد. Nginx از .htaccess پشتیبانی نمی کند و این قانون را نادیده می گیرد. وقتی کسی /about/ را باز می کند، Nginx دنبال فایلی به همین نام می گردد، پیدا نمی کند و خودش 404 می دهد، بدون این که وردپرس خبردار شود. با تغییر دادن تنظیمات پیوندهای یکتا در پیشخوان هم این حالت درست نمی شود، چون مشکل از سرور وب است. معادل همین کار در Apache را در ریدایرکت HTTPS و www با htaccess می بینید.

کانفیگ کامل server block برای وردپرس

فایل را در /etc/nginx/sites-available/example.com.conf بسازید. این مسیر ساختار Ubuntu و Debian است. در توزیع های RedHat محور معمولا فایل را در /etc/nginx/conf.d/example.com.conf می گذارند. مقدارهای root، server_name و مسیر socket را با سرور خودتان عوض کنید:

server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;

    root /var/www/example.com/public_html;
    index index.php index.html;

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

    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/php8.2-fpm.sock;
    }

    # hidden files such as .htaccess and .git, except .well-known for SSL
    location ~ /\.(?!well-known) {
        deny all;
    }

    # no PHP execution inside uploads
    location ~* ^/wp-content/uploads/.*\.php$ {
        deny all;
    }

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

    location = /robots.txt {
        log_not_found off;
        access_log off;
    }
}

هر بخش یک کار مشخص دارد. دستور try_files $uri $uri/ /index.php?$args اول دنبال فایل می گردد، بعد پوشه، و اگر هیچ کدام نبود درخواست را با همان query string به index.php وردپرس می دهد. بلوک location ~ \.php$ فایل های PHP را به PHP-FPM می فرستد. بلوک سوم هر فایل پنهان را می بندد، مثل .htaccess، .git و .env، ولی .well-known را باز می گذارد تا تایید دامنه برای گواهی SSL کار کند. بلوک چهارم مانع اجرای اسکریپت PHP داخل پوشه آپلودها می شود، که راه رایج جاسازی بدافزار است.

نام socket به نسخه PHP نصب شده بستگی دارد، پس ls /run/php/ را اجرا کنید و نام واقعی را بنویسید. اگر PHP-FPM به جای socket روی TCP گوش می دهد، به جای آن fastcgi_pass 127.0.0.1:9000; بگذارید. بعد از ساخت فایل، در Ubuntu آن را فعال کنید:

sudo ln -s /etc/nginx/sites-available/example.com.conf /etc/nginx/sites-enabled/

اعمال امن تغییرات با nginx -t و reload

پیش از هر تغییر روی سرور فعال، از فایل کانفیگ فعلی یک کپی بگیرید، مثلا sudo cp example.com.conf example.com.conf.bak. سپس همیشه اول تست کنید و بعد reload کنید. reload با کانفیگ خراب رد می شود و Nginx با تنظیمات قبلی ادامه می دهد، ولی restart با کانفیگ خراب ممکن است Nginx را اصلا بالا نیاورد و همه سایت ها از دسترس بروند.

sudo nginx -t
sudo systemctl reload nginx

اگر nginx -t پیام syntax is ok و test is successful داد، reload را بزنید. در غیر این صورت خطا شماره خط فایل را می دهد و باید همان را اصلاح کنید. دقت کنید که هر فایل در sites-enabled وارد کانفیگ اصلی می شود، پس یک ; جا افتاده در یک سایت، همه را مختل می کند. برای دیدن کانفیگ نهایی که Nginx واقعا بارگذاری کرده، sudo nginx -T را اجرا کنید.

خطاهای رایج و علت آن ها

این جدول مشکلات مرتبط با کانفیگ Nginx برای وردپرس را به ترتیب شیوع کنار هم می گذارد:

علامتعلت محتملراه حل
صفحه اصلی باز می شود و بقیه آدرس ها 404 صفحه خود Nginx می دهندtry_files در location اصلی نیستخط try_files $uri $uri/ /index.php?$args; را داخل location / بگذارید
متن File not found. به صورت سادهمسیر root اشتباه است یا SCRIPT_FILENAME درست ساخته نمی شودroot را بررسی کنید و include snippets/fastcgi-php.conf; را داشته باشید
فایل PHP دانلود می شودبلوک location ~ \.php$ وجود ندارد یا اجرا نمی شودبلوک را اضافه کنید و ترتیب location ها را بررسی کنید
502 Bad Gatewayنام یا مسیر socket اشتباه است، یا PHP-FPM بالا نیستls /run/php/ و systemctl status php8.2-fpm را ببینید
سایت دیگری باز می شودserver_name با دامنه نمی خواندserver_name را اصلاح کنید و با nginx -T تکرار نشدنش را بررسی کنید
تصاویر و CSS باز نمی شوندمسیر root یا مجوز پوشه هامالک و دسترسی فایل ها را برای کاربر pool PHP-FPM بررسی کنید

اگر به جای 404 خطای 502 دیدید، راهنمای خطای 502 Bad Gateway در Nginx مرحله به مرحله جلو می رود. اگر صفحه ای بعد از مدتی انتظار 504 می دهد، به رفع خطای 504 Gateway Timeout برگردید.

کش فایل های استاتیک

وردپرس فایل های CSS، جاوااسکریپت و تصویر زیادی دارد که تغییر نمی کنند. اگر Nginx به مرورگر بگوید آن ها را نگه دارد، رفت و برگشت ها کم می شود. بلوک زیر را بعد از بلوک PHP اضافه کنید:

location ~* \.(css|js|jpg|jpeg|png|gif|webp|svg|ico|woff2)$ {
    expires 30d;
    access_log off;
}

مدت 30 روز یک انتخاب معمول است و اگر فایل های قالب را زیاد عوض می کنید، با اضافه شدن شماره نسخه به آدرس فایل مشکلی پیش نمی آید. فشرده سازی gzip در بسیاری از بسته های Nginx در nginx.conf اصلی روشن یا کامنت شده است. برای دانستن وضعیت واقعی، فایل /etc/nginx/nginx.conf را باز کنید و ببینید gzip on; فعال است یا نه. کش صفحات کامل با FastCGI cache یا افزونه جدا بحث می شود و در این راهنما نیست. برای مقایسه ای میان Nginx و وب سرورهایی که .htaccess را مستقیم می خوانند، مقایسه LiteSpeed و Apache برای وردپرس را ببینید.

مالکیت و مجوز فایل ها

بعد از انتقال سایت، مالکیت فایل ها اغلب با کاربری که PHP-FPM با آن اجرا می شود یکی نیست. نتیجه اش خطای آپلود، ناتوانی در به روزرسانی افزونه و گاهی 403 است. کاربر pool را در فایل pool (مثلا /etc/php/8.2/fpm/pool.d/www.conf، خطوط user و group) ببینید. در Ubuntu معمولا www-data است. توصیه عمومی وردپرس این است که پوشه ها 755 و فایل ها 644 باشند و wp-config.php دسترسی محدودتری داشته باشد. این دستورها را در مسیر ریشه سایت اجرا می کنند:

sudo chown -R www-data:www-data /var/www/example.com/public_html
sudo find /var/www/example.com/public_html -type d -exec chmod 755 {} \;
sudo find /var/www/example.com/public_html -type f -exec chmod 644 {} \;

پیش از اجرا مطمئن شوید مسیر درست است، چون chown -R روی مسیر اشتباه خطرناک است.

وردپرس در زیرپوشه

اگر وردپرس در یک زیرپوشه مثل /blog/ نصب شده و سایت اصلی چیز دیگری است، try_files باید به index.php همان زیرپوشه اشاره کند:

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

بدون این تغییر، درخواست به index.php ریشه سایت می رسد و پیوندهای یکتا باز هم 404 می دهند.

محدود کردن حمله به صفحه ورود

صفحه wp-login.php هدف همیشگی حدس زدن رمز است. Nginx با ماژول limit_req تعداد درخواست هر IP را محدود می کند. ابتدا در بلوک http فایل nginx.conf یک zone تعریف کنید:

limit_req_zone $binary_remote_addr zone=wplogin:10m rate=1r/s;

سپس داخل server block سایت، برای همان آدرس اعمالش کنید:

location = /wp-login.php {
    limit_req zone=wplogin burst=5 nodelay;
    include snippets/fastcgi-php.conf;
    fastcgi_pass unix:/run/php/php8.2-fpm.sock;
}

این محدودیت جایگزین افزونه امنیتی یا رمز قوی نیست و فقط بار حمله های خودکار را کم می کند. اگر چند کاربر پشت یک IP مشترک (مثلا یک شبکه شرکت) همزمان وارد می شوند، مقدار burst را بیشتر بگذارید.

یک بلوک برای www و بدون www

در مثال بالا هر دو نام دامنه در یک server block آمده اند، ولی بهتر است یکی از آن ها آدرس اصلی باشد و دیگری ریدایرکت شود تا محتوای تکراری ساخته نشود. برای این کار یک server block جدا برای نام ثانویه بنویسید:

server {
    listen 80;
    server_name www.example.com;
    return 301 http://example.com$request_uri;
}

سپس server_name بلوک اصلی را فقط example.com بگذارید. بعد از گرفتن گواهی SSL، همین الگو را برای https هم تکرار کنید. دقت کنید آدرس سایت در تنظیمات عمومی وردپرس (WordPress Address و Site Address) با همان نام اصلی یکی باشد، وگرنه وردپرس خودش یک ریدایرکت دیگر می سازد و حلقه ریدایرکت پیش می آید.

بعد از اعمال کانفیگ

چند کار ساده کانفیگ را تایید می کند. از پیشخوان وردپرس به تنظیمات، پیوندهای یکتا بروید و یک بار روی ذخیره بزنید. این کار در Nginx فایلی ننوشته و فقط قوانین داخلی را تازه می کند، ولی برای خیال راحت لازم است. بعد یک نوشته را با آدرس کامل در مرورگر باز کنید و با دستور زیر کد پاسخ را ببینید:

curl -I https://example.com/sample-post/

باید HTTP/2 200 یا HTTP/1.1 200 OK بگیرید. آدرس یک فایل پنهان مثل /.htaccess باید 403 برگرداند و یک فایل PHP که داخل wp-content/uploads گذاشته اید باید اجرا نشود.

SSL، آپلود و ادامه تنظیم سرور

این کانفیگ فقط روی پورت 80 گوش می دهد. برای HTTPS گواهی را با Certbot بگیرید که خودش بلوک 443 را اضافه می کند. روند کار در نصب Certbot و Let's Encrypt روی Nginx و Apache توضیح داده شده است. بعد از آن اگر صفحات هشدار محتوای ناامن می دهند، رفع mixed content در وردپرس را ببینید.

سقف آپلود در این کانفیگ همان یک مگابایت پیش فرض Nginx است و اولین بار که فایل بزرگی آپلود کنید 413 می گیرید. رفع آن در رفع خطای 413 Request Entity Too Large آمده است. مستندات Nginx برای هر دستور ذکر شده در nginx.org هست و وردپرس هم نمونه کانفیگ رسمی خودش را در wordpress.org منتشر کرده است. برای فهرست کارهایی که پیش از راه اندازی سایت باید بررسی شوند، چک لیست هاست وردپرس را بخوانید.