I’m running a Laravel backend alongside a Vue frontend on NGINX. The issue I’m facing is that my API endpoints are returning HTML instead of JSON. For example:
https://isuecampusmap.site/api/→ returns JSON correctly (health check works)https://isuecampusmap.site/api/buildings→ returns HTML or “File not found”
Here’s what I’ve already tried:
Verified that
routes/api.phpcontains the correct routes (/api/buildings,/api/rooms, etc.).Confirmed that
public/index.phpexists in the Laravel backend.Cleared Laravel caches (
php artisan config:clear,route:clear,cache:clear).Adjusted NGINX config to separate Vue (
/) and Laravel (/api).Tested with
curl -H "Accept: application/json"but still get HTML for most routes.
Question: Why are my Laravel API routes (like /api/buildings) being served as HTML instead of JSON? Is this an NGINX misconfiguration, or is Sanctum middleware blocking unauthenticated requests? What’s the correct way to configure NGINX so that all /api/* routes are passed to Laravel’s index.php and return JSON responses?
Any guidance or working config examples would be greatly appreciated.
Environment:
Laravel 11.27.2
PHP 8.3.6
NGINX 1.24.0 (Ubuntu)
Ubuntu 24.04 LTS
My NGINX config:
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name isuecampusmap.site www.isuecampusmap.site;
ssl_certificate /etc/letsencrypt/live/isuecampusmap.site/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/isuecampusmap.site/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
# Vue Frontend
root /var/www/isuecampusmap/isueadminpanelnewvue/dist;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
# Laravel API
location /api/ {
alias /var/www/isuecampusmap/backend/public/;
index index.php;
try_files $uri $uri/ /index.php?$query_string;
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass unix:/var/run/php/php8.3-fpm.sock;
fastcgi_param SCRIPT_FILENAME $request_filename;
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
}
}
# Cache static assets
location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# Deny hidden files
location ~ /\.(?!well-known) {
deny all;
}
}
/apiroute.