- Home
- HTTP Methods
- HTTP POST Method: Complete Guide with Examples
Method
HTTP POST Method: Complete Guide with Examples
Learn how the HTTP POST method works. Understand when to use POST requests, request bodies, form submissions, and API calls with practical examples.
TL;DR: POST sends data to create new resources or trigger actions on the server. Use it for form submissions, user registration, file uploads, and API calls that modify data.
POST is the HTTP method for sending data to servers. When you submit a form, upload a file, or create a new account, you’re using POST. Unlike GET which retrieves data, POST sends data to create or process something new.
What is POST?
POST requests include data in the request body, telling the server to process it and typically create a new resource:
POST /api/users HTTP/1.1
Host: api.example.com
Content-Type: application/json
Content-Length: 64
{
"name": "Alice Johnson",
"email": "alice@example.com"
}
```text
The server processes the data and responds:
```http
HTTP/1.1 201 Created
Location: /api/users/456
Content-Type: application/json
{
"id": 456,
"name": "Alice Johnson",
"email": "alice@example.com",
"createdAt": "2026-01-19T10:30:00Z"
}
Key Characteristics
Non-Safe
POST requests modify server state. They create, update, or trigger actions:
POST /orders → Creates a new order
POST /payments → Processes a payment
POST /emails/send → Sends an email
Non-Idempotent
Sending the same POST request twice may have different effects:
POST /orders with {product: "Book"}
→ First request: Creates order #1001
→ Second request: Creates order #1002 (duplicate!)
This is why browsers show “Confirm Form Resubmission” warnings.
Not Cached
POST responses are not cached by default. Each request goes to the server:
Cache-Control: no-store
```text
### Data in Body
POST data is sent in the request body, not the URL:
```http
GET /search?q=secret ← Visible in URL, logs, history
POST /search with body ← Hidden from URL
```text
## Content Types
The `Content-Type` header tells the server how to parse the request body.
### JSON (APIs)
Most common for modern APIs:
```http
POST /api/users HTTP/1.1
Content-Type: application/json
{
"name": "Alice",
"email": "alice@example.com",
"roles": ["user", "admin"]
}
Form URL-Encoded (HTML Forms)
Default for HTML <form> submissions:
POST /login HTTP/1.1
Content-Type: application/x-www-form-urlencoded
username=alice&password=secret123
```text
### Multipart Form Data (File Uploads)
Required for file uploads:
```http
POST /upload HTTP/1.1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary
------WebKitFormBoundary
Content-Disposition: form-data; name="file"; filename="photo.jpg"
Content-Type: image/jpeg
[binary image data]
------WebKitFormBoundary
Content-Disposition: form-data; name="description"
My vacation photo
------WebKitFormBoundary--
Plain Text
Simple text data:
POST /logs HTTP/1.1
Content-Type: text/plain
Error occurred at line 42: undefined variable
```text
## Real-World Examples
### User Registration
```http
POST /api/auth/register HTTP/1.1
Host: api.example.com
Content-Type: application/json
{
"email": "newuser@example.com",
"password": "securePassword123",
"name": "New User"
}
Response:
HTTP/1.1 201 Created
Location: /api/users/789
Set-Cookie: session=abc123; HttpOnly; Secure
{
"id": 789,
"email": "newuser@example.com",
"name": "New User",
"createdAt": "2026-01-19T10:30:00Z"
}
```text
### Login Form
```http
POST /api/auth/login HTTP/1.1
Host: api.example.com
Content-Type: application/json
{
"email": "user@example.com",
"password": "mypassword"
}
Response:
HTTP/1.1 200 OK
Content-Type: application/json
{
"token": "eyJhbGciOiJIUzI1NiIs...",
"expiresIn": 3600,
"user": {
"id": 123,
"email": "user@example.com"
}
}
```text
### Creating a Blog Post
```http
POST /api/posts HTTP/1.1
Host: api.example.com
Content-Type: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
{
"title": "Understanding HTTP POST",
"content": "POST is used to send data...",
"tags": ["http", "tutorial"],
"published": true
}
File Upload
POST /api/files/upload HTTP/1.1
Host: api.example.com
Content-Type: multipart/form-data; boundary=----Boundary
Authorization: Bearer token123
------Boundary
Content-Disposition: form-data; name="file"; filename="document.pdf"
Content-Type: application/pdf
[PDF binary data]
------Boundary--
```text
### Webhook Notification
```http
POST /webhooks/payment HTTP/1.1
Host: yourapp.com
Content-Type: application/json
X-Webhook-Signature: sha256=abc123...
{
"event": "payment.completed",
"data": {
"orderId": "ORD-123",
"amount": 99.99,
"currency": "USD"
}
}
Response Status Codes
| Code | Meaning | When to Use |
|---|---|---|
| 200 OK | Success | Action completed, returning data |
| 201 Created | Resource created | New resource created successfully |
| 202 Accepted | Accepted | Request queued for processing |
| 204 No Content | Success, no body | Action completed, nothing to return |
| 400 Bad Request | Invalid data | Malformed JSON or missing fields |
| 401 Unauthorized | Auth required | Missing or invalid credentials |
| 403 Forbidden | Access denied | Not allowed to perform action |
| 409 Conflict | Conflict | Resource already exists |
| 422 Unprocessable | Validation failed | Data format correct but invalid |
| 429 Too Many Requests | Rate limited | Slow down requests |
POST vs GET
| Aspect | POST | GET |
|---|---|---|
| Purpose | Send/create data | Retrieve data |
| Data location | Request body | URL query string |
| Visible in URL | No | Yes |
| Cached | No | Yes |
| Bookmarkable | No | Yes |
| Safe | No | Yes |
| Idempotent | No | Yes |
| Data size | Unlimited | ~2,000 chars |
| Binary data | Yes | No |
| Sensitive data | Better | Never |
POST vs PUT
| Aspect | POST | PUT |
|---|---|---|
| Purpose | Create resource | Replace resource |
| Idempotent | No | Yes |
| URL | Collection (/users) | Specific (/users/123) |
| Multiple calls | Creates duplicates | Same result |
| Client knows ID | No | Yes |
POST /users → Server assigns ID (creates user #456)
PUT /users/456 → Client specifies ID (replaces user #456)
```nginx
## Common Headers
### Request Headers
| Header | Purpose | Example |
| ---------------- | ------------------------ | ------------------ |
| `Content-Type` | Body format | `application/json` |
| `Content-Length` | Body size | `256` |
| `Authorization` | Auth credentials | `Bearer token123` |
| `Accept` | Expected response format | `application/json` |
| `X-Request-ID` | Request tracking | `req-abc-123` |
### Response Headers
| Header | Purpose | Example |
| ----------------------- | ---------------- | ------------------ |
| `Location` | New resource URL | `/api/users/456` |
| `Content-Type` | Response format | `application/json` |
| `X-RateLimit-Remaining` | Requests left | `99` |
## Best Practices
### Do's ✅
```javascript
// Use POST for creating resources
const response = await fetch('/api/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'Alice', email: 'alice@example.com' })
})
// Always set Content-Type
headers: { 'Content-Type': 'application/json' }
// Handle the response properly
if (response.status === 201) {
const newUser = await response.json()
const location = response.headers.get('Location')
}
// Validate data before sending
if (!email || !password) {
throw new Error('Email and password required')
}
Don’ts ❌
// Don't use POST for retrieving data
POST /api/users/123 // Wrong! Use GET
// Don't forget Content-Type
fetch('/api/users', {
method: 'POST',
body: JSON.stringify(data) // Server won't know it's JSON!
})
// Don't send sensitive data without HTTPS
http://api.example.com/login // Insecure!
https://api.example.com/login // Correct!
// Don't ignore error responses
const response = await fetch('/api/users', { method: 'POST', ... })
const data = await response.json() // Might be an error!
```javascript
## JavaScript Examples
### Basic POST
```javascript
const response = await fetch('https://api.example.com/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer token123'
},
body: JSON.stringify({
name: 'Alice',
email: 'alice@example.com'
})
})
if (response.ok) {
const newUser = await response.json()
console.log('Created user:', newUser.id)
}
Form Submission
const form = document.querySelector('form')
const formData = new FormData(form)
const response = await fetch('/api/submit', {
method: 'POST',
body: formData // Content-Type set automatically
})
```javascript
### File Upload
```javascript
const fileInput = document.querySelector('input[type="file"]')
const formData = new FormData()
formData.append('file', fileInput.files[0])
formData.append('description', 'My upload')
const response = await fetch('/api/upload', {
method: 'POST',
body: formData
})
Error Handling
async function createUser(userData) {
const response = await fetch('/api/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(userData)
})
if (response.status === 201) {
return await response.json()
}
if (response.status === 400) {
const error = await response.json()
throw new Error(`Validation error: ${error.message}`)
}
if (response.status === 409) {
throw new Error('User already exists')
}
throw new Error(`Unexpected error: ${response.status}`)
}
Try It Yourself
Create a new resource with our interactive request builder:
- Select POST method
- Set path to
/posts - Add JSON body:
{"title": "Test", "body": "Hello"} - Click Send and observe the 201 response
Related Methods
- GET - Retrieve resources
- PUT - Replace entire resources
- PATCH - Partial updates
- DELETE - Remove resources
Related Concepts
- Content-Type - Specifying body format
- 201 Created - Success response for POST
- Request Body - POST data structure
- CORS - Cross-origin POST requests
In Practice
Express.js
// POST /users — create a new user
app.post('/users', async (req, res) => {
const { name, email, password } = req.body
const user = await db.users.create({ name, email, password })
res.status(201)
.location(`/users/${user.id}`)
.json(user)
})
// POST /sessions — login
app.post('/sessions', async (req, res) => {
const user = await db.users.findByEmail(req.body.email)
if (!user || !user.verifyPassword(req.body.password)) {
return res.status(401).json({ error: 'Invalid credentials' })
}
const token = generateToken(user)
res.json({ token })
})Next.js App Router
// app/api/users/route.ts
export async function POST(request: Request) {
const body = await request.json()
const user = await db.users.create(body)
return Response.json(user, {
status: 201,
headers: { Location: `/api/users/${user.id}` }
})
}
// app/api/sessions/route.ts
export async function POST(request: Request) {
const { email, password } = await request.json()
const user = await db.users.findByEmail(email)
if (!user?.verifyPassword(password)) {
return Response.json({ error: 'Invalid credentials' }, { status: 401 })
}
return Response.json({ token: generateToken(user) })
}Django
# views.py
import json
from django.http import JsonResponse
from django.views.decorators.http import require_POST
from django.views.decorators.csrf import csrf_exempt
@csrf_exempt
@require_POST
def create_user(request):
data = json.loads(request.body)
user = User.objects.create(
name=data['name'],
email=data['email'],
)
response = JsonResponse(user.to_dict(), status=201)
response['Location'] = f'/users/{user.id}'
return responseFrequently Asked Questions
What is the HTTP POST method?
POST is an HTTP method used to send data to a server to create or update a resource. Unlike GET, POST includes data in the request body rather than the URL, making it suitable for form submissions, file uploads, and API calls that modify data.
When should I use POST instead of GET?
Use POST when creating new resources, submitting forms, uploading files, or sending sensitive data like passwords. POST data is sent in the request body (not visible in URL), is not cached, and can handle large payloads without URL length limits.
Is POST secure?
POST is more secure than GET for sensitive data because the data is not visible in the URL, browser history, or server logs. However, POST data is still sent in plain text unless you use HTTPS. Always use HTTPS for sensitive information.
What Content-Type should I use with POST?
Common Content-Types are application/json for API calls, application/x-www-form-urlencoded for HTML forms, and multipart/form-data for file uploads. The Content-Type header tells the server how to parse the request body.
Is POST idempotent?
No, POST is not idempotent. Sending the same POST request multiple times may create multiple resources or have different effects each time. This is why browsers warn you when refreshing a page after a POST submission.
What status code should a POST request return?
201 Created is ideal when a new resource is created, with a Location header pointing to the new resource. 200 OK is used when the action succeeded but no new resource was created. 204 No Content indicates success with no response body.