HTTP

Status Code

HTTP 403 Forbidden: Access Denied

Learn what 403 Forbidden means, why access is denied, and how to fix permission errors. Complete guide with examples for APIs and web applications.

3 min read beginner Try in Playground

TL;DR: 403 Forbidden means access denied. You’re authenticated but don’t have permission for this resource.

TL;DR: 403 means “I know who you are, but you can’t access this.” You’re authenticated but not authorized.

What is 403 Forbidden?

A 403 error means the server knows who you are but refuses access. You don’t have permission.

Think of it like having a valid employee badge but trying to enter a restricted area—security knows you work there, but you’re not allowed in that room.

GET /admin/users HTTP/1.1
Authorization: Bearer valid_user_token
```text

```http
HTTP/1.1 403 Forbidden
Content-Type: application/json

{"error": "forbidden", "message": "Admin access required"}

401 vs 403

Aspect401 Unauthorized403 Forbidden
Question”Who are you?""Are you allowed?”
ProblemAuthenticationAuthorization
SolutionLog inGet permissions
function authorize(req, res, next) {
  if (!req.user) {
    return res.status(401).json({ error: 'Authentication required' })
  }
  if (!req.user.roles.includes('admin')) {
    return res.status(403).json({ error: 'Admin access required' })
  }
  next()
}
```javascript

## When 403 Happens

- **Insufficient role** - Regular user accessing admin endpoint
- **Wrong ownership** - Accessing another user's private data
- **Missing API scope** - Token lacks required permissions
- **IP restrictions** - Request from blocked IP/region
- **File permissions** - Server file chmod too restrictive

## How to Fix

### For Users

1. Request access from resource owner
2. Verify you're accessing the correct resource
3. Check if your account has required role/plan

### For Developers

```javascript
// Check ownership before access
async function getDocument(req, res) {
  const doc = await Document.findById(req.params.id)

  if (!doc) return res.status(404).json({ error: 'Not found' })

  if (doc.ownerId !== req.user.id && !req.user.isAdmin) {
    return res.status(403).json({ error: 'Access denied' })
  }

  res.json(doc)
}

403 vs 404 for Security

// 403: Reveals resource exists
if (resource.ownerId !== req.user.id) {
  return res.status(403).json({ error: 'Access denied' })
}

// 404: Hides resource existence (more secure)
if (!resource || resource.ownerId !== req.user.id) {
  return res.status(404).json({ error: 'Not found' })
}

Use 404 for sensitive resources where existence itself is private information.

403 vs 404: Information Leakage Tradeoffs

The choice between 403 and 404 for unauthorized access involves a security tradeoff. Returning 403 confirms that the resource exists but the user cannot access it. Returning 404 hides whether the resource exists at all. For sensitive resources, 404 is often the more secure choice because it prevents attackers from enumerating which resources exist.

Consider a multi-tenant application where each user has private documents. If an attacker tries to access /documents/12345 and receives 403, they know document 12345 exists and belongs to someone else. If they receive 404, they cannot distinguish between “this document does not exist” and “this document exists but you cannot see it.” This makes it harder to enumerate other users’ resources.

The tradeoff is user experience. A legitimate user who accidentally navigates to a resource they do not have access to will see a confusing “Not Found” error instead of a clear “Access Denied” message. For internal tools and admin interfaces where users understand the permission model, 403 is more helpful. For public-facing APIs where resource existence is sensitive, 404 is more secure. Document your choice in your API design guidelines so it is applied consistently.

In Practice

Express.js
// Role-based access control middleware
function requireRole(...roles) {
  return (req, res, next) => {
    if (!req.user) {
      return res.status(401).json({ error: 'Authentication required' })
    }
    if (!roles.includes(req.user.role)) {
      return res.status(403).json({
        error: 'Forbidden',
        message: `Requires role: ${roles.join(' or ')}`
      })
    }
    next()
  }
}

app.delete('/api/users/:id', requireAuth, requireRole('admin'), async (req, res) => {
  await db.users.delete(req.params.id)
  res.status(204).end()
})
Next.js App Router
// app/api/admin/users/route.ts
export async function DELETE(request: Request) {
  const user = await getAuthUser(request)
  if (!user) {
    return Response.json({ error: 'Unauthorized' }, { status: 401 })
  }
  if (user.role !== 'admin') {
    return Response.json(
      { error: 'Forbidden', message: 'Admin access required' },
      { status: 403 }
    )
  }
  // Proceed with deletion...
  return new Response(null, { status: 204 })
}
Django
from django.http import JsonResponse
from django.contrib.auth.decorators import login_required

def require_role(*roles):
    def decorator(view_func):
        @login_required  # Returns 401 if not authenticated
        def wrapper(request, *args, **kwargs):
            if request.user.role not in roles:
                return JsonResponse(
                    {'error': 'Forbidden', 'message': f'Requires role: {roles}'},
                    status=403
                )
            return view_func(request, *args, **kwargs)
        return wrapper
    return decorator

@require_role('admin')
def admin_delete_user(request, user_id):
    User.objects.filter(pk=user_id).delete()
    return JsonResponse({}, status=204)

Frequently Asked Questions

What does 403 Forbidden mean?

A 403 error means the server knows who you are but refuses to authorize access. You're authenticated but lack permission for this resource.

What is the difference between 401 and 403?

401 means "who are you?" (authentication). 403 means "you can't access this" (authorization). Login helps with 401, not 403.

How do I fix a 403 error?

Request access from the resource owner, verify you have the correct role/permissions, or check if your API token has required scopes.

Should I use 403 or 404 for unauthorized access?

Use 403 to confirm resource exists but deny access. Use 404 to hide resource existence for security.

Can logging in fix a 403 error?

No. 403 means you're already authenticated but lack authorization. You need elevated permissions, not login.

Keep Learning