پروژه روی سیستم خودت عالی کار میکنه، میبریش روی سرور و اولین فرمی که میفرستی صفحهی 419 Page Expired رو نشونت میده. کد همونه، پس مشکل از کجاست؟
جواب کوتاه: لاراول توکن CSRF رو داخل session نگه میداره و session هم با یه کوکی به مرورگر وصله. اگه اون کوکی بین دو تا درخواست گم بشه یا عوض بشه، توکن فرم دیگه با توکن session یکی نیست و لاراول درخواست رو رد میکنه. پس وقتی فقط روی سرور 419 میگیری، اول برو سراغ کوکی و HTTPS، نه کد فرم.
@csrf داخل فرم هست؟ یه View Source بگیر و ببین فیلد مخفی _token تو فرم هست یا نه.
درخواستهای AJAX توکن میفرستن؟ اگه با fetch کار میکنی، توی <head> یه تگ <meta name="csrf-token" content="{{ csrf_token() }}"> بذار و مقدارش رو تو هدر X-CSRF-TOKEN بفرست. Axios روی همون دامنه این کار رو خودش با کوکی XSRF-TOKEN انجام میده.
اگه این دو تا درست بودن و باز هم فقط روی سرور خطا داری، برو بخش بعد.
سایت روی https بالا اومده ولی تو .env هنوز نوشته APP_URL=http://.... این رو درست کن و بعدش حتماً کش کانفیگ رو پاک کن:
php artisan optimize:clear
خیلی وقتها HTTPS روی Nginx یا Cloudflare تموم میشه و درخواست با http به PHP میرسه. لاراول خیال میکنه اتصال ناامنه و کوکی رو بدون Secure میسازه. دو جا باید درست بشه.
تو Nginx هدر پروتکل رو پاس بده:
proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $host;
و تو لاراول ۱۱ به بعد، داخل bootstrap/app.php پراکسی رو trust کن:
->withMiddleware(function (Middleware $middleware) { $middleware->trustProxies(at: '*'); })
اگه میتونی بهجای * آیپی پراکسیهات رو بذار، امنتره. برای چک کردن نتیجه، روی سرور ببین request()->isSecure() مقدار true میده یا نه.
این یکی رو زیاد دیدم. تو حالت Flexible، مرورگر با Cloudflare روی HTTPS حرف میزنه ولی Cloudflare با سرورت روی HTTP. نتیجه همون داستان کوکیه. حالت SSL رو بذار روی Full یا Full (Strict).
برای سایتی که فقط روی HTTPS کار میکنه، این ترکیب معمولاً جواب میده:
SESSION_SECURE_COOKIE=true SESSION_SAME_SITE=lax SESSION_HTTP_ONLY=true
SESSION_HTTP_ONLY رو هیچوقت false نکن؛ اونوقت جاوااسکریپت هم میتونه کوکی session رو بخونه.
اگه اپ روی یه سابدامینه یا میخوای session بین چند سابدامین مشترک باشه، SESSION_DOMAIN رو درست ست کن. مقدار .example.com با نقطهی اول یعنی کوکی برای همهی سابدامینها معتبره. بعد از تغییر، تو DevTools بخش Application و بعد Cookies رو باز کن و دامنه و تیک Secure کوکی رو با چشم خودت ببین.
اگه درایور session روی file هست و پوشهی storage/framework/sessions دسترسی نوشتن نداره، هر درخواست یه session تازه میسازه. اگه روی database هست، مطمئن شو جدول sessions ساخته شده.
تو Livewire هم ریشه همینه، فقط بیشتر به چشم میاد چون هر کلیک یه درخواست AJAX میفرسته. یه حالت رایجش اینه که کاربر صفحه رو مدت طولانی باز نگه میداره و session منقضی میشه. مقدار SESSION_LIFETIME رو ببین و اگه خیلی کمه، بیشترش کن. بعد از آپگرید لاراول یا Livewire هم یه php artisan optimize:clear و رفرش کامل صفحه رو فراموش نکن.
وسوسهی اینکه CSRF رو برای همهی مسیرها خاموش کنی رو کنار بذار. این یعنی هر سایت دیگهای میتونه از طرف کاربر لاگینکردهات فرم بفرسته. تنها جای منطقی برای استثنا، وبهوک سرویسهای بیرونیه که توکن نمیفرستن، و اونجا هم باید امضای درخواست رو چک کنی.
@csrf و متا تگ توکن سر جاشونن.
APP_URL با https نوشته شده و کش کانفیگ پاک شده.
Nginx هدر X-Forwarded-Proto رو میفرسته و لاراول پراکسی رو trust کرده.
Cloudflare روی Full یا Full (Strict) هست.
کوکی session تو مرورگر تیک Secure و دامنهی درست داره.
پوشه یا جدول session قابل نوشتنه.
این یادداشت رو برای مخاطبهای کاپیتان کد نوشتم. اگه 419 رو با یه علت دیگه حل کردی، تو نظرات بنویس که بقیه هم استفاده کنن.