Laravel 9部署Amazon Linux EC2时文件上传报403 Forbidden错误排查求助
Laravel 9 EC2部署后文件上传返回HTTP 403错误排查指南
已知情况
- 本地环境文件上传功能正常,部署到Amazon Linux EC2服务器后,上传图片操作返回HTTP 403错误,无异常抛出(已开启
.env的debug模式) - 仅上传图片时触发错误,其他控制器功能运行正常
疑似问题
403错误在到达ContactController的update方法前触发(已在方法第一行添加dd()但未执行),推测问题出在Apache/服务器配置层面。
已执行排查步骤
- 确认S3存储桶策略正常(本地同控制器可正常操作S3)
- 验证服务器根目录权限无误
- 确认
UpdateContactRequest请求类的authorize()方法返回true - 查看错误日志未发现相关记录
- 确认
php.ini配置:file_uploads=On、upload_max_filesize=4M、max_file_uploads=20
相关代码与配置
控制器代码
ContactController的update方法
//https://myapp.com/contacts/1 //App\Http\Controllers\ContactController public function update(UpdateContactRequest $request, Contact $contact) { dd($request); //未执行,403在到达此处前触发 $contact->update($request->all()); $this->uploadAvatar($request, $contact); Flash::success('Contact updated successfully.'); return redirect(route('dealers.contacts.index', $contact->dealer->id)); }
uploadAvatar函数
private function uploadAvatar(Request $request, Contact $contact) { if ($request->hasFile('avatar')) { try { $contact ->addMediaFromRequest('avatar') ->sanitizingFileName(function ($fileName) { return strtolower(str_replace(['#', '/', '\\', ' '], '-', $fileName)); }) ->toMediaCollection('avatars'); } catch (\Spatie\MediaLibrary\MediaCollections\Exceptions\FileUnacceptableForCollection $e) { Flash::error($e->getMessage()); } } }
路由定义
Route::get('/', function () { return view('welcome'); }); Route::get('/test', App\Http\Controllers\TestController::class); Route::get('/embed-iframe/{uuid}', [App\Http\Controllers\EmbedController::class, 'iframe']); Route::get('/embed-js/{uuid}', [App\Http\Controllers\EmbedController::class, 'js']); Auth::routes(); Route::middleware('admin')->group(function () { Route::get('/home', [App\Http\Controllers\HomeController::class, 'index'])->name('home'); Route::resource('dealers', App\Http\Controllers\DealerController::class); Route::post('refreshDealerCRMData', [App\Http\Controllers\DealerController::class, 'refreshCRMData']); Route::post('loadCRMView', [App\Http\Controllers\DealerController::class, 'loadCRMView']); Route::resource('cms', App\Http\Controllers\CmsController::class); Route::resource('crms', App\Http\Controllers\CrmController::class); Route::resource('leads', App\Http\Controllers\LeadController::class); Route::resource('contacts', App\Http\Controllers\ContactController::class); Route::resource('attachment-categories', App\Http\Controllers\AttachmentCategoryController::class); Route::resource('CRMAttachments', App\Http\Controllers\CRMAttachmentController::class); Route::resource('dealers.leads', App\Http\Controllers\DealerLeadController::class)->scoped([ 'dealers' => 'dealer.id', 'leads' => 'lead.id', ]); Route::resource('dealers.contacts', App\Http\Controllers\DealerContactController::class)->scoped([ 'dealers' => 'dealer.id', 'contacts' => 'contact.id', ]); });
服务器配置
根目录权限

Apache虚拟主机配置(/etc/httpd/conf/httpd.conf)
<VirtualHost *:443> ServerAdmin user@domain.com ServerName myapp.com ServerAlias www.myapp.com DocumentRoot /var/www/html/public <Directory /var/www/html> Options Indexes FollowSymLinks MultiViews AllowOverride All Require all granted </Directory> </VirtualHost> <VirtualHost *:80> ServerAdmin user@domain.com ServerName myapp.com ServerAlias www.myapp.com DocumentRoot /var/www/html/public <Directory /var/www/html> Options Indexes FollowSymLinks MultiViews AllowOverride All Require all granted </Directory> </VirtualHost>
IAM策略
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:PutObject", "s3:ListBucket", "s3:GetObject" ], "Resource": [ "arn:aws:s3:::mybucket", "arn:aws:s3:::mybucket/*" ] } ] }
错误截图

进一步排查步骤
检查Apache mod_security模块
- 执行
httpd -M | grep security确认是否启用mod_security - 若启用,临时修改
/etc/httpd/conf.d/mod_security.conf,将SecRuleEngine On改为SecRuleEngine Off,重启Apache:systemctl restart httpd,测试上传是否恢复正常 - 若正常,需调整mod_security规则,允许合法文件上传请求,或针对上传路由添加排除规则
- 执行
验证PHP临时上传目录权限
- 检查默认临时目录
/tmp权限:ls -ld /tmp,确保Apache运行用户(通常为apache)有读写权限 - 若权限不足,执行
chmod 1777 /tmp修复,或在php.ini中指定自定义临时目录并配置正确权限
- 检查默认临时目录
检查SELinux状态
- 执行
sestatus查看SELinux状态,若为enforcing模式,临时设置为permissive测试:setenforce 0 - 若上传正常,需配置SELinux规则允许Apache操作:
- 允许Laravel存储目录写入:
chcon -R -t httpd_sys_rw_content_t /var/www/html/storage - 允许临时目录访问:
chcon -t httpd_tmp_t /tmp
- 允许Laravel存储目录写入:
- 执行
查看Apache访问日志
- 检查
/var/log/httpd/access_log中对应403请求的记录,查看是否有mod_security拦截标记或其他错误说明
- 检查
测试极简上传接口
- 创建独立测试路由,排除Laravel验证逻辑干扰:
Route::post('/test-upload', function(Request $request) { if($request->hasFile('avatar')) { return response()->json(['success' => true, 'filename' => $request->file('avatar')->getClientOriginalName()]); } return response()->json(['success' => false]); }); - 用Postman或curl发送请求,判断是服务器层面还是Laravel层面的问题
- 创建独立测试路由,排除Laravel验证逻辑干扰:
验证post_max_size配置
- 检查
php.ini中的post_max_size值,需大于upload_max_filesize,否则会导致大文件请求被拦截
- 检查
内容的提问来源于stack exchange,提问作者farjam
相关产品推荐
相关产品推荐

