Skip to main content
How To Fix a 403 Forbidden Error on Your VPS - Virtarix Blog

How To Fix a 403 Forbidden Error on Your VPS

October 7, 2025 · Blog / Technical Guides

A 403 Forbidden response means a server or intermediary understood the request but refused it. The denial can come from web-server rules, filesystem ownership or permissions, an application or security plugin, a CDN or web application firewall, an authentication policy, or a mandatory access-control system such as SELinux.

The useful question is therefore not “which permission command fixes 403?” It is “which layer generated this particular 403, and what evidence identifies the rule that denied it?” This guide follows that question from the client and edge to the origin, application, and operating system.

On Virtarix, Linux VPS plans include full root access, but website configuration, application changes, log diagnosis, security rules, and recovery remain customer-managed.

Last checked: 12 August 2026.

What a 403 response does and does not prove

RFC 9110 defines 403 as a refusal to fulfil the request. It does not prove that the requested file exists, that Unix permissions are wrong, or that the origin VPS generated the response. A CDN, reverse proxy, web server, application, or access-control module can all return the same status.

A 401 response means the request lacks valid authentication credentials for the target resource. A 404 means the server did not find the current representation, although a server may deliberately use 404 instead of 403 to hide a forbidden resource. Diagnose the response you can reproduce rather than inferring the cause from the number alone.

Troubleshoot the 403 by layer

Before changing configuration, record the exact URL, HTTP method, client IP, time with timezone, current response headers, and the last known-good state. Preserve the relevant configuration and application data, keep an authorized administrative session open, and define how to restore the exact rule, ownership, permission, plugin, or policy you change. Make one controlled change at a time.

Confirm scope

Reproduce the denial with the same method and path. Compare an affected browser with a private window and a second network only when the policy is expected to vary by cookie, identity, or source address. Capture headers without assuming that a hard refresh repairs server-side authorization:

Capture response headers and timing
curl --silent --show-error --dump-header - --output /dev/null \
  --write-out 'status=%{http_code} remote_ip=%{remote_ip} time=%{time_total}\n' \
  https://example.com/protected-path

Record whether every path fails, one directory fails, one HTTP method fails, or only authenticated/unauthenticated requests fail. Test the origin directly only when you can preserve the intended Host header and TLS validation; bypassing a CDN with a casual IP request can exercise a different virtual host and produce misleading evidence.

Response headers can identify the responding layer. A CDN request identifier, proxy-specific header, branded block page, or origin Server header is a clue, not final proof. Correlate it with edge events and origin logs at the same timestamp.

Web server rules

Start with the active virtual host and error log, not a guessed document root. For Apache, list virtual-host mappings and validate configuration before a reload:

Inspect and validate Apache virtual hosts
apache2ctl -S
apache2ctl configtest

For Nginx, dump the effective configuration and validate it:

Inspect and validate Nginx configuration
nginx -T
nginx -t

Search the matched server/location/directory context for explicit denies, missing grants, authentication requirements, method restrictions, absent index handling, and symlink policy. Apache 2.4 authorization uses directives such as Require all granted or a deliberately scoped Require ip; the Apache access-control guide explains how these rules combine. The Nginx access module evaluates allow and deny rules in order.

Do not paste a universal allow rule into .htaccess or disable an access module merely to make the status disappear. Locate the rule that applies to the failing path, preserve it, make the narrowest correction, validate syntax, and reload rather than restart when the platform supports a safe reload.

If Apache permits per-directory overrides, temporarily moving a site-owned .htaccess file can be a controlled test only after preserving its name, owner, mode, and contents. Restore it immediately if the result does not identify the cause. Apache's configuration-file documentation explains the relationship between the main configuration and .htaccess files.

Filesystem ownership/permissions

First identify the effective document root, web-server worker user, and every parent directory in the failing path. Inspect rather than recursively rewriting the whole tree:

Inspect path traversal and ownership
namei -l /var/www/example/public/index.php
stat -c '%A %a %U:%G %n' /var/www/example/public/index.php
ps -eo user,group,comm,args | grep -E '[a]pache2|[h]ttpd|[n]ginx|[p]hp-fpm'

The worker needs search permission on each parent directory and the access required for the target. The correct owner and mode depend on the deployment model: a static site, an application with writable upload directories, and a release deployed by a separate user should not share one blanket www-data:www-data ownership rule.

Compare the failing path with a working sibling and the application's documented permission model. Correct only the mismatched path. Avoid recursive chmod 755, chmod 644, or chown across an unknown web root: those commands can expose secrets, remove required executable bits, or break deployment ownership.

After a scoped correction, repeat namei and stat, then request the same URL. If the 403 persists or a new write/runtime error appears, restore the recorded owner and mode before testing another layer.

Application/CMS

An application can intentionally return 403 for a route, role, nonce, session, origin, method, CSRF check, maintenance rule, or security policy even when the web server can read every file. Correlate the request timestamp and application request ID with the application log. Check the route and user role, recent deployments, configuration changes, plugin/module updates, and application caches.

Do not disable every plugin or security control on production at once. Reproduce the request in a staging or maintenance window, disable one suspected component or rule, verify the result, and restore it if the evidence does not change.

WordPress-specific checks

Treat WordPress as one application case, not the universal 403 fix. Review the affected user's role, security-plugin events, REST/nonces when relevant, and the site's own .htaccess or Nginx rules. If access to wp-admin remains available, resaving permalinks can regenerate WordPress rewrite rules; preserve custom directives before doing so.

WordPress's file-permissions guidance emphasizes that permissions depend on the hosting and ownership model. Do not copy a recursive permission recipe without identifying which user writes updates and which user serves the files.

CDN/WAF/security layer

If the edge produced the 403, inspect its security events for the exact timestamp, client IP, hostname, path, method, and request/rule identifier. Determine whether a custom WAF rule, managed rule, bot control, rate limit, country/IP restriction, signed URL, or cached response denied the request.

Cloudflare's 403 troubleshooting documentation distinguishes branded edge-generated 403 responses from unbranded origin responses. Other providers expose different identifiers, so use that provider's event log rather than assuming every 403 with a proxy in front came from the proxy.

Adjust only the rule that matched and keep its intended protection. Prefer a scoped exception for the verified hostname/path/method or a corrected rule expression over turning off the WAF, lowering site-wide security, purging every cache, or permanently allowlisting a changing client IP.

Authentication/access policy

Check the policy that protects the exact resource: HTTP basic authentication, SSO groups, application roles, client certificates, signed URLs, source-IP lists, and method-specific authorization can all deny a request. Confirm which identity the server received rather than which identity the user expected to send.

For IP-based rules, record the address observed at the enforcing layer. Reverse proxies can change the apparent client address, and trusting forwarded headers from arbitrary sources creates a separate security problem. For Fail2Ban or another ban system, inspect the named jail/rule and its evidence before removing a ban; if repeated failed authentication caused it, correct that behavior rather than creating a permanent broad allowlist.

SELinux where applicable

On an SELinux system, ordinary Unix modes can be correct while the security context denies the web server. Confirm enforcement, inspect labels, and correlate recent AVC denials:

Inspect SELinux state and web-root labels
getenforce
ls -Zd /var/www/example /var/www/example/public
journalctl --since '-15 minutes' | grep -i 'avc:.*denied'

Compare the path with a known-working path and the distribution's web-server policy. If the intended persistent file-context mapping already exists, preview what restorecon would change before applying it:

Preview SELinux label restoration
restorecon -nRv /var/www/example/public

The restorecon manual documents dry-run and verbose behavior. Correct the persistent mapping when the application uses a nonstandard path; do not solve the incident by permanently disabling SELinux or leaving the host in permissive mode.

Verify and roll back

Repeat the original request with the same method, hostname, path, identity, and relevant source network. A successful public homepage does not verify a previously forbidden upload, API method, admin route, or authenticated resource.

Confirm all of the following:

  • the intended request now returns its expected status and content;
  • a user or source that should remain forbidden is still denied;
  • web-server, application, CDN/WAF, and security logs show the expected decision;
  • configuration validation passes and the service survives a safe reload;
  • application reads and required writes still work; and
  • no secret, private file, directory listing, or broader route became public.

If the check fails, restore the single recorded rule, file, owner, mode, plugin state, or policy before forming the next hypothesis. Record the symptom, evidence, exact change, result, final state, and follow-up owner.

Prevent the next unexplained 403

Keep web-server and application configuration in version control, validate it before reload, and deploy with an explicit ownership/permission model. Preserve custom WordPress or per-directory rules during migrations. Give CDN/WAF changes named owners and change records. Centralize logs with synchronized time, request identifiers, and retention long enough to investigate an incident.

Monitor the real protected path and method where practical, not only the homepage. Test both allowed and intentionally denied requests after authentication, WAF, plugin, deployment, or filesystem changes. A documented known-good response and rollback point turns the next 403 from a permission guessing exercise into a bounded diagnosis.

Ready to operate a self-managed website VPS?

Virtarix supplies the VPS infrastructure and full root access on Linux plans. You remain responsible for website configuration, permissions, application security, logs, updates, monitoring, independent copies, recovery, and incident diagnosis.

VPS S

For small websites and landing pages

$ 5 .50 /month
  • 3 cores
  • 6 GB
  • 50 GB NVMe
  • Unlimited
Get It Now
BEST SELLER

VPS M

For growing sites and staging

$ 11 .40 /month
  • 6 cores
  • 16 GB
  • 100 GB NVMe
  • Unlimited
Get It Now
Peter French
About the Author Peter Frenchis the Managing Director at Virtarix, with over 17 years in the tech industry. He has co-founded a cloud storage business, led strategy at a global cloud computing leader, and driven market growth in cybersecurity and data protection.