HTTP

Method

HTTP OPTIONS Method

Learn how HTTP OPTIONS requests discover server capabilities, supported methods, and handle CORS preflight checks for cross-origin requests.

5 min read intermediate

What is OPTIONS?

TL;DR: OPTIONS discovers what methods and operations are allowed for a resource. Browsers use it automatically for CORS preflight checks before cross-origin requests.

OPTIONS is the HTTP method for discovering what a server can do with a specific resource. Think of it like asking “what operations are allowed here?” before actually performing them.

OPTIONS is most commonly used for CORS (Cross-Origin Resource Sharing) preflight checks, but it’s also useful for API discovery and capability testing.

Key Characteristics

1. Safe

OPTIONS requests don’t change anything on the server. They’re purely informational.

2. Idempotent

Making the same OPTIONS request multiple times produces the same result with no side effects.

3. Discovery Purpose

OPTIONS tells you what methods, headers, and operations are allowed for a resource.

4. CORS Preflight

Browsers automatically send OPTIONS requests before certain cross-origin requests to check if they’re allowed.

How OPTIONS Works

1. Client asks about capabilities:

OPTIONS /posts HTTP/1.1
Host: api.example.com
Origin: https://myapp.com
```http

**2. Server responds with allowed operations:**

```http
HTTP/1.1 200 OK
Allow: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Origin: https://myapp.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400
Content-Length: 0

Real-World Examples

Example 1: CORS Preflight Check

When your JavaScript tries to make a cross-origin request with custom headers, the browser first sends an OPTIONS request:

OPTIONS /api/posts HTTP/1.1
Host: api.example.com
Origin: https://myapp.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type, Authorization
```http

**Server response:**

```http
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://myapp.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 3600

Example 2: API Discovery

OPTIONS /users/42 HTTP/1.1
Host: api.example.com
```text

**Response:**

```http
HTTP/1.1 200 OK
Allow: GET, PUT, PATCH, DELETE, OPTIONS
Content-Type: application/json

{
  "methods": ["GET", "PUT", "PATCH", "DELETE"],
  "description": "User resource operations",
  "requiredAuth": true
}

Example 3: Checking Specific Resource

OPTIONS /files/upload HTTP/1.1
Host: storage.example.com
```http

**Response:**

```http
HTTP/1.1 200 OK
Allow: POST, OPTIONS
Accept: image/*, video/*, application/pdf
Max-Content-Length: 10485760

Example 4: WebDAV Server Capabilities

OPTIONS /documents/ HTTP/1.1
Host: webdav.example.com
```http

**Response:**

```http
HTTP/1.1 200 OK
Allow: GET, PUT, DELETE, PROPFIND, PROPPATCH, MKCOL, COPY, MOVE
DAV: 1, 2
Content-Length: 0

Example 5: Wildcard OPTIONS

OPTIONS * HTTP/1.1
Host: api.example.com
```http

**Response:**

```http
HTTP/1.1 200 OK
Allow: GET, POST, PUT, DELETE, HEAD, OPTIONS
Server: nginx/1.18.0
API-Version: 2.1.0

When to Use OPTIONS

✅ Use OPTIONS for:

  • CORS preflight checks (automatic by browsers)
  • Discovering allowed methods for a resource
  • Testing API capabilities before implementation
  • Checking server features and extensions
  • Validating permissions before operations
  • API documentation and introspection

❌ Don’t use OPTIONS for:

  • Retrieving actual resource data (use GET)
  • Creating resources (use POST)
  • Updating resources (use PUT or PATCH)
  • Deleting resources (use DELETE)

CORS Preflight Explained

When Browsers Send Preflight

Browsers automatically send OPTIONS preflight requests for:

  1. Non-simple methods: PUT, DELETE, PATCH, etc.
  2. Custom headers: Authorization, X-Custom-Header, etc.
  3. Non-simple content types: application/json, etc.

Simple vs Preflight Requests

Simple request (no preflight needed):

fetch('https://api.example.com/posts', {
  method: 'GET',
  headers: {
    Accept: 'application/json'
  }
})
```text

**Complex request (preflight required):**

```javascript
fetch('https://api.example.com/posts', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    Authorization: 'Bearer token123'
  },
  body: JSON.stringify({ title: 'New Post' })
})

The browser sends this OPTIONS request first:

OPTIONS /posts HTTP/1.1
Host: api.example.com
Origin: https://myapp.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type, Authorization
```http

## Common Response Headers

### CORS Headers

```http
Access-Control-Allow-Origin: https://myapp.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400
Access-Control-Allow-Credentials: true

Method Information

Allow: GET, POST, PUT, DELETE, OPTIONS
Accept: application/json, application/xml
Content-Type: application/json
```http

### Server Capabilities

```http
Server: Apache/2.4.41
API-Version: 2.1.0
Rate-Limit-Max: 1000
Rate-Limit-Window: 3600

Response Patterns

1. Basic Method List

HTTP/1.1 200 OK
Allow: GET, POST, PUT, DELETE, OPTIONS
Content-Length: 0
```http

### 2. CORS Preflight Response

```http
HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 3600
Content-Length: 0

3. Detailed Capabilities

HTTP/1.1 200 OK
Allow: GET, POST, PUT, DELETE
Content-Type: application/json

{
  "methods": {
    "GET": "Retrieve resource",
    "POST": "Create new resource",
    "PUT": "Replace resource",
    "DELETE": "Remove resource"
  },
  "authentication": "required",
  "rateLimit": "1000 requests per hour"
}
```text

## Common Response Codes

| Code    | Meaning            | When Used                                   |
| ------- | ------------------ | ------------------------------------------- |
| **200** | OK                 | Methods and capabilities returned           |
| **204** | No Content         | Allowed methods in headers, no body         |
| **400** | Bad Request        | Invalid OPTIONS request                     |
| **401** | Unauthorized       | Authentication required to see capabilities |
| **403** | Forbidden          | Not allowed to query capabilities           |
| **404** | Not Found          | Resource doesn't exist                      |
| **405** | Method Not Allowed | OPTIONS not supported                       |

## Best Practices

**1. Always support OPTIONS for CORS**

```javascript
// Express.js example
app.options('/api/*', (req, res) => {
  res.header('Access-Control-Allow-Origin', 'https://myapp.com')
  res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE')
  res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization')
  res.header('Access-Control-Max-Age', '86400')
  res.sendStatus(200)
})

2. Include comprehensive method information

app.options('/users/:id', (req, res) => {
  const user = findUser(req.params.id)

  if (!user) {
    return res.status(404).json({ error: 'User not found' })
  }

  const allowedMethods = ['GET', 'OPTIONS']

  // Add methods based on permissions
  if (canEdit(req.user, user)) {
    allowedMethods.push('PUT', 'PATCH')
  }

  if (canDelete(req.user, user)) {
    allowedMethods.push('DELETE')
  }

  res.header('Allow', allowedMethods.join(', '))
  res.json({
    methods: allowedMethods,
    permissions: getUserPermissions(req.user, user)
  })
})
```text

**3. Set appropriate cache headers**

```javascript
app.options('/api/*', (req, res) => {
  res.header('Access-Control-Max-Age', '86400') // Cache for 24 hours
  res.header('Vary', 'Origin') // Cache varies by origin
  res.sendStatus(200)
})

4. Handle wildcard requests

app.options('*', (req, res) => {
  res.header('Allow', 'GET, POST, PUT, DELETE, HEAD, OPTIONS')
  res.header('Server', 'MyAPI/1.0')
  res.header('API-Version', '2.1.0')
  res.sendStatus(200)
})
```text

## CORS Configuration Examples

### Permissive CORS (Development)

```javascript
app.use((req, res, next) => {
  res.header('Access-Control-Allow-Origin', '*')
  res.header('Access-Control-Allow-Methods', '*')
  res.header('Access-Control-Allow-Headers', '*')
  next()
})

Restrictive CORS (Production)

const allowedOrigins = ['https://myapp.com', 'https://admin.myapp.com']

app.use((req, res, next) => {
  const origin = req.headers.origin

  if (allowedOrigins.includes(origin)) {
    res.header('Access-Control-Allow-Origin', origin)
  }

  res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE')
  res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization')
  res.header('Access-Control-Allow-Credentials', 'true')
  next()
})
```javascript

## Debugging CORS Issues

### Check Preflight Request

```javascript
// Log all OPTIONS requests
app.options('*', (req, res, next) => {
  console.log('OPTIONS request:', {
    origin: req.headers.origin,
    method: req.headers['access-control-request-method'],
    headers: req.headers['access-control-request-headers']
  })
  next()
})

Common CORS Problems

Problem: “CORS policy: No ‘Access-Control-Allow-Origin’ header” Solution: Add proper CORS headers to OPTIONS response

Problem: “CORS policy: Method PUT is not allowed” Solution: Include PUT in Access-Control-Allow-Methods

Problem: “CORS policy: Request header Authorization is not allowed” Solution: Include Authorization in Access-Control-Allow-Headers

Try It Yourself

Visit our request builder to experiment with OPTIONS:

  1. Select OPTIONS as the method
  2. Try path /posts to see allowed methods
  3. Add Origin header: https://example.com
  4. Watch the CORS headers in the response!
  • GET - Retrieve resources
  • POST - Create resources
  • PUT - Replace resources
  • DELETE - Remove resources
  • CORS - Cross-Origin Resource Sharing
  • Headers - Request and response headers
  • Status Codes - Understanding response codes

In Practice

Express.js
// CORS preflight — Express handles OPTIONS via cors() middleware
const cors = require('cors')

app.use(cors({
  origin: ['https://app.example.com'],
  methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'],
  allowedHeaders: ['Content-Type', 'Authorization'],
  maxAge: 86400 // Cache preflight for 24 hours
}))

// Manual OPTIONS handler (if not using cors middleware)
app.options('/api/*', (req, res) => {
  res.set({
    'Access-Control-Allow-Origin': 'https://app.example.com',
    'Access-Control-Allow-Methods': 'GET, POST, PUT, PATCH, DELETE',
    'Access-Control-Allow-Headers': 'Content-Type, Authorization',
    'Access-Control-Max-Age': '86400'
  }).status(204).end()
})
Next.js App Router
// app/api/users/route.ts
// Handle CORS preflight explicitly
export async function OPTIONS() {
  return new Response(null, {
    status: 204,
    headers: {
      'Access-Control-Allow-Origin': 'https://app.example.com',
      'Access-Control-Allow-Methods': 'GET, POST, PUT, PATCH, DELETE',
      'Access-Control-Allow-Headers': 'Content-Type, Authorization',
      'Access-Control-Max-Age': '86400'
    }
  })
}

// Or configure globally in next.config.ts headers()
Go net/http
func corsMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Access-Control-Allow-Origin", "https://app.example.com")
        w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE")
        w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")

        if r.Method == http.MethodOptions {
            w.Header().Set("Access-Control-Max-Age", "86400")
            w.WriteHeader(http.StatusNoContent)
            return
        }
        next.ServeHTTP(w, r)
    })
}

Frequently Asked Questions

What is the HTTP OPTIONS method?

OPTIONS requests the communication options available for a resource. It returns allowed methods in the Allow header and is used for CORS preflight requests.

What is a CORS preflight request?

Browsers send OPTIONS before certain cross-origin requests to check if the actual request is allowed. The server responds with CORS headers indicating permissions.

What headers does OPTIONS return?

Allow header lists permitted methods. For CORS: Access-Control-Allow-Methods, Access-Control-Allow-Headers, Access-Control-Max-Age.

Is OPTIONS safe and idempotent?

Yes, OPTIONS is both safe and idempotent. It only retrieves information and never modifies server state.

Keep Learning