Skip to content

Amount Must Never Come From the Browser

Amount Must Never Come From the Browser is a checkout security rule: payment amount, price, plan, discounts and buyer identity must be derived on the server, not accepted from client request fields. The browser may express intent, but the server must recompute what is payable before creating a payment or subscription.

The browser is fully under the user’s control, including parts your interface makes look fixed. Hidden inputs, disabled fields, JSON payloads, query strings, local storage and cookies can all be changed before a request reaches your API. The same is true for fields generated by frontend code. A checkout endpoint that trusts those values is not just validating badly, it is allowing the customer to define the commercial terms of the transaction.

The safer design is to let the client send identifiers and intent, not facts about money. For example, the request can say which product or plan the user wants. The server then loads the authenticated user, checks ownership or eligibility, fetches the current price, applies allowed discounts, calculates tax and currency, and creates the payment provider session from those server-side results. The provider should receive an amount your backend computed, not one the browser supplied.

This moves complexity into the backend, and that is the trade-off. You need authoritative product and pricing data, clear rules for discounts and trials, and server-side checks for account, subscription and quantity changes. Cached prices, experiments and regional pricing make the answer depend on the user, time and context. That dependence is exactly why copying an amount from the client is unsafe: the backend is the only place that can resolve it consistently.

Engineers meet this issue when building checkout, subscription upgrades, invoice payment, seat changes, coupon flows and account switching. In code review, inspect every checkout request field as attacker-controlled unless you can prove it was created and authorised server-side. Common red flags include client-sent amount, currency, plan, account id, subscription id, discount eligibility or customer id being passed directly into a payment API.

Common questions

Can the browser send a price if the backend validates it?
It can, but the price should not be treated as the source of truth. If the backend receives a price, it should compare it with a server-side calculation and reject mismatches. In most designs, sending the price is unnecessary. Send a product or plan reference, then let the server compute the payable amount.
Are hidden fields or signed frontend values safe for checkout amounts?
Hidden fields are not safe; they are just ordinary request data. Signed values can detect tampering, but they do not solve stale pricing, eligibility, account ownership or business rule changes unless the signature covers the right context and expiry. The simpler, more robust pattern is server-side lookup and authorisation at checkout time.
What checkout fields should be considered client-controlled?
Assume everything in the request is client-controlled: body fields, headers, query parameters, cookies, route parameters and values your own frontend generated. Price, amount, currency, plan, quantity, coupon, account id, subscription id and customer identity should be recomputed, looked up or authorised on the server before any payment provider call.