Blog
network
3 min read

Respect the Semantics: Designing GET and POST with Safety and Idempotency

GET is not just a "URL loading method," but has a "read-only semantic." By respecting this, caching, prefetching, and retries become safe. (RFC 9110)

GET is not just a “URL loading method,” but has a “read-only semantic.” Respecting these definitions from RFC 9110 makes caching, prefetching, and retries safer and your API more predictable.


Background: Why Semantics Matter

If a developer uses GET for destructive actions (e.g., GET /delete-user?id=123), it can lead to catastrophic accidents because search engines or middleware might "prefetch" these URLs.


Core Concepts: Safe and Idempotent

1) Safe Methods (Read-Only)

A “Safe” method is one that does not change the state of the server (e.g., GET, HEAD, OPTIONS).

  • Accident Example: If a “Logout” or “Delete” action is a GET link, a browser extension or a web crawler could accidentally log out a user or delete data just by scanning the page.

2) Idempotent Methods (Same result for multiple calls)

An “Idempotent” method ensures that multiple identical requests have the same effect as a single request (e.g., PUT, DELETE).

  • POST vs. PUT: POST is not idempotent (calling it twice creates two items), whereas PUT is idempotent (calling it twice with the same data leads to the same final state).

Quick Summary Table

MethodSafe (Read-only)Idempotent (Same result)
GET✅ Yes✅ Yes
POST❌ No❌ No
PUT❌ No✅ Yes
DELETE❌ No✅ Yes

Solution Approach

  1. Use GET only for retrieval: Ensure that calling a GET endpoint never alters server data.
  2. Use POST for creation: For actions that are not idempotent and create resources, always use POST.
  3. Use PUT/DELETE for idempotency: When you want to ensure that multiple calls result in the same state, use PUT (replace) or DELETE.

Implementation (Code Example)

Comparison of a dangerous GET for state change and a proper POST:

javascript
// ❌ DANGEROUS: Destructive action via GET\n// Links can be clicked by crawlers or pre-rendered.\napp.get('/accounts/delete', (req, res) => { ... });\n\n// ✅ SECURE: Destructive action via POST/DELETE\n// Requires a deliberate action (form submission or API call).\napp.delete('/accounts', (req, res) => { ... });

Reproducing in Next.js (Execution via Button on Client)

javascript
\"use client\"\n\nimport { useState } from 'react';\n\nexport default function MethodSemanticsDemo() {\n  const [log, setLog] = useState(\"\");\n\n  const handleGetDanger = () => {\n    setLog(\"Warning: A 'Safe' GET request was used for a destructive action. Prefetching would trigger this!\");\n  };\n\n  return (\n    <div className=\"p-4 flex flex-col gap-4\">\n      <button onClick={handleGetDanger} className=\"bg-yellow-500 text-white p-2\">Simulate Unsafe GET Click</button>\n      <div className=\"mt-2 italic\">{log}</div>\n    </div>\n  );\n}

This demo highlights that treating GET as more than just a data carrier is crucial for web security and network efficiency.


Common Mistakes/FAQ

Q1. Is it okay to send sensitive data via GET parameters?

No. GET parameters are stored in the browser history, server logs, and can be seen in the URL. Always use POST with a Request Body for sensitive information like passwords or tokens.

Q2. Does POST automatically make an API secure?

Using the correct method is about semantics (meaning), not just security. However, sticking to the standard allows security tools and browsers to handle your site more appropriately.


Summary

  • GET is for reading (Safe).
  • POST is for creating (Not idempotent).
  • PUT/DELETE are for updating/removing (Idempotent).

Conclusion

RFC 9110 is the blueprint for the web. By aligning your API design with these standards, you ensure that your application is robust, scalable, and follows the fundamental rules of the internet.


Related Posts