HTTP
410 - Gone
Seeing a 410 Gone means the resource was permanently deleted and won't come back—unlike 404 (might exist elsewhere), this explicitly signals intentional, permanent removal. This client-side error (4xx) happens when servers want to distinguish between "not found" (404) and "was here, now deleted forever" (410). Most common when users delete content, temporary resources expire, or URLs are intentionally removed, but also appears in APIs that implement soft-delete workflows or resource lifecycle management.
#Common Causes
- →Frontend: User tries to access deleted content via old bookmark or cached URL. Stale cache references resource that was deleted. Deep links to expired temporary resources (upload links, preview URLs). Client retries request for resource that was intentionally removed.
- →Backend: Resource soft-deleted and marked as permanently removed. Temporary resource expired beyond recovery window. Admin intentionally removed content. Database record deleted with cascade, resource permanently gone. Business logic enforces permanent deletion (GDPR compliance, user request).
- →Infrastructure: CDN cache serves stale 410 responses. Load balancer routes to backend instance that deleted resource while others still have it. Database replication lag causes temporary inconsistencies before 410 is consistent.
✓Solutions
- 1Step 1: Diagnose - Check DevTools Network tab Response body—410 responses often include deletedAt timestamp or reason. Verify if this is expected (user deleted content) or unexpected. Check if resource ID is valid but deleted.
- 2Step 2: Diagnose - Server logs show deletion timestamp and reason. Review soft-delete records to confirm permanent removal. Check database for deleted_at timestamp. Verify if resource is recoverable or truly gone.
- 3Step 3: Fix - Client-side: Remove resource from local cache and UI immediately. Show user-friendly "This content has been permanently deleted" message. Update navigation to remove dead links. Implement cache invalidation on 410 responses.
- 4Step 4: Fix - Server-side: Return 410 with helpful metadata (deletedAt, reason, alternative resources if available). Set proper cache headers (Cache-Control: no-store) to prevent caching. Implement proper soft-delete workflows.
- 5Step 5: Fix - Infrastructure: Ensure CDN respects no-cache headers for 410 responses. Verify load balancer routes consistently. Clear CDN cache after deletions. Implement database cleanup jobs for truly permanent deletions.
</>Code Examples
Fetch API: Handle 410 Gone Responses
1// Client-side: Handle 410 Gone with cache cleanup
2async function fetchResource(resourceId) {
3 const response = await fetch(`/api/posts/${resourceId}`);
4
5 if (response.status === 410) {
6 const errorData = await response.json();
7
8 // Remove from cache
9 localStorage.removeItem(`cache-post-${resourceId}`);
10 sessionStorage.removeItem(`post-${resourceId}`);
11
12 // Remove from UI
13 const element = document.getElementById(`post-${resourceId}`);
14 if (element) {
15 element.remove();
16 }
17
18 // Show user-friendly message
19 showNotification(
20 errorData.message || 'This content has been permanently deleted',
21 'info'
22 );
23
24 // Optionally redirect or update navigation
25 updateNavigation();
26
27 return null;
28 }
29
30 if (!response.ok) {
31 throw new Error(`Request failed: ${response.status}`);
32 }
33
34 const data = await response.json();
35
36 // Cache successful responses
37 localStorage.setItem(`cache-post-${resourceId}`, JSON.stringify(data));
38
39 return data;
40}Express.js: Soft Delete with 410 Response
1// Server-side: Implement soft-delete with 410 Gone
2const express = require('express');
3const app = express();
4
5// Get resource (returns 410 if soft-deleted)
6app.get('/api/posts/:id', async (req, res) => {
7 const post = await db.posts.findById(req.params.id);
8
9 if (!post) {
10 return res.status(404).json({ error: 'Not Found' });
11 }
12
13 // Check if soft-deleted
14 if (post.deletedAt) {
15 return res
16 .status(410)
17 .set('Cache-Control', 'no-store, must-revalidate')
18 .json({
19 error: 'Gone',
20 message: 'This post has been permanently deleted',
21 deletedAt: post.deletedAt,
22 reason: post.deletionReason || 'Deleted by user',
23 });
24 }
25
26 res.set('ETag', generateETag(post));
27 res.json(post);
28});
29
30// Soft delete (marks as deleted, returns 410)
31app.delete('/api/posts/:id', async (req, res) => {
32 const post = await db.posts.findById(req.params.id);
33
34 if (!post) {
35 return res.status(404).json({ error: 'Not Found' });
36 }
37
38 // Soft delete
39 await db.posts.update(req.params.id, {
40 deletedAt: new Date(),
41 deletionReason: req.body.reason || 'Deleted by user',
42 });
43
44 // Return 410 Gone to indicate permanent deletion
45 res.status(410).json({
46 message: 'Post permanently deleted',
47 deletedAt: new Date().toISOString(),
48 });
49});Nginx: Cache Control for 410 Responses
1# Nginx: Ensure 410 responses aren't cached
2server {
3 listen 80;
4 server_name api.example.com;
5
6 location /api/posts/ {
7 proxy_pass http://backend;
8 proxy_set_header Host $host;
9 proxy_set_header X-Real-IP $remote_addr;
10
11 # Don't cache 410 responses
12 proxy_cache_valid 410 0s;
13 proxy_cache_bypass $upstream_http_status;
14
15 # Add no-cache headers for 410
16 add_header Cache-Control "no-store, must-revalidate" always;
17
18 # Pass through backend cache headers
19 proxy_pass_header Cache-Control;
20 proxy_pass_header ETag;
21 }
22}↗Related Errors
Provider Information
This error code is specific to HTTP services. For more information, refer to the official HTTP documentation.