What Happens If You Only Check Access on the Client?
Imagine you launch a Web3 project with premium content that's only accessible to NFT holders. A week later, the content leaks into the open. The reason? Client-side token ownership verification, easily bypassed. This happens to every second startup: according to statistics, 80% of projects with client-side protection lose exclusivity within a month.
The solution is server-side validation. It checks token ownership at the blockchain level, and content never reaches the client without authorization. This eliminates leaks entirely and reduces server load by 40% through caching verification results. We implement token-gated pages and site sections turnkey with guaranteed security and performance. Our experience in Web3 development — over 5 years, 20+ projects — allows us to choose the optimal strategy for your task.
Main Protection Strategies
We consider three main strategies: Hard Gate (complete blocking without a token), Soft Gate (blurred content with overlay), and Progressive Disclosure (partial access). Each has its advantages and use cases.
| Strategy | Security | UX | Server Load | Implementation Complexity |
|---|---|---|---|---|
| Hard Gate | High (server 403) | Negative (blocking) | Low (content not loaded) | Medium |
| Soft Gate | Low (content loaded but blurred) | Positive (sees preview) | High (all data loaded) | Low |
| Progressive Disclosure | Medium (mixed check) | Best (partial display) | Medium (depends on rules) | High |
When to Use Soft Gate Instead of Hard Gate?
Soft Gate is justified when the main goal is attraction and conversion. The user sees that content exists and is motivated to obtain the token. Hard Gate is better for commercial data (analytics, private chats) where leaks are unacceptable. We combine approaches: on the main page — Soft Gate, in the personal account — Hard Gate.
How Does Progressive Disclosure Improve Conversion?
Progressive Disclosure shows part of the content for free (e.g., headings, short description), and full access is opened by token. This increases engagement: the user sees value and is ready to purchase an NFT. On educational platforms, conversion increases by 30–40%.
Hard Gate: Maximum Protection
Hard Gate is a server-side check on every request. If the user is not authenticated or does not have the token, the server returns a 403 or redirects to the wallet connection page. Suitable for premium content: analytics, private chats, exclusive materials. We use JWT tokens to store the session after verification, reducing the number of requests to the blockchain. At first mention ERC-721 is the standard for NFTs.
Soft Gate (Blur Gate) — Marketing Approach
Soft Gate loads content on the client but displays it blurred with an overlay calling for access. Often used as a marketing tool: the user sees what they are missing. However, content is technically available in the DOM, so it is not recommended for commercial data. We add additional protection via CSS pointer-events and blur, but when security requirements are high, we choose Hard Gate.
Progressive Disclosure: Balance Between Openness and Exclusivity
Progressive Disclosure is a combined approach: part of the content is open to all (e.g., headings, previews), and full access is by token. Well suited for courses, articles where you need to attract users. Implemented through a combination of server and client checks. We use React Server Components to render protected parts on the server.
How We Implement Token-Gated Pages
Server-Side Protection in Next.js (App Router)
// app/members/page.tsx (Next.js App Router)
import { cookies } from 'next/headers';
import { redirect } from 'next/navigation';
import { verifyTokenGate } from '@/lib/token-gate';
export default async function MembersPage() {
const cookieStore = cookies();
const token = cookieStore.get('auth_token')?.value;
if (!token) {
redirect('/connect-wallet?redirect=/members');
}
const { walletAddress } = verifyJwt(token);
const hasAccess = await verifyTokenGate(walletAddress, {
contractAddress: process.env.NFT_CONTRACT,
type: 'ERC721',
minBalance: 1
});
if (!hasAccess) {
redirect('/token-required?contract=' + process.env.NFT_CONTRACT);
}
return <MembersContent />;
}
UI Components for Client Side
// components/TokenGate.tsx
import { useAccount, useReadContract } from 'wagmi';
import { erc721Abi } from 'viem';
interface TokenGateProps {
contractAddress: `0x${string}`;
tokenType: 'ERC721' | 'ERC20';
minBalance?: bigint;
lockedContent: React.ReactNode; // отображается при отсутствии токена
children: React.ReactNode;
}
export function TokenGate({
contractAddress, tokenType, minBalance = 1n, lockedContent, children
}: TokenGateProps) {
const { address, isConnected } = useAccount();
const { data: balance, isLoading } = useReadContract({
address: contractAddress,
abi: erc721Abi,
functionName: 'balanceOf',
args: [address!],
query: { enabled: isConnected && !!address }
});
if (!isConnected) {
return <WalletConnectPrompt redirectAfter={window.location.pathname} />;
}
if (isLoading) {
return <div className="token-gate-loading">Проверка доступа...</div>;
}
const hasAccess = (balance ?? 0n) >= minBalance;
if (!hasAccess) {
return <>{lockedContent}</>;
}
return <>{children}</>;
}
// Использование
function PremiumSection() {
return (
<TokenGate
contractAddress="0xYourNFTContract"
tokenType="ERC721"
lockedContent={
<div className="token-gate-overlay">
<h3>Только для держателей NFT</h3>
<p>Купите NFT для получения доступа к эксклюзивному контенту</p>
<a href="https://opensea.io/collection/your-nft">Купить на OpenSea</a>
</div>
}
>
<ExclusiveContent />
</TokenGate>
);
}
Blur-gate Effect
// Размытый preview с оверлеем
function BlurGate({ hasAccess, children, contractAddress }) {
return (
<div className="relative">
<div className={hasAccess ? '' : 'blur-sm select-none pointer-events-none'}>
{children}
</div>
{!hasAccess && (
<div className="absolute inset-0 flex items-center justify-center bg-black/30 backdrop-blur-sm">
<div className="bg-white rounded-xl p-8 text-center shadow-xl max-w-sm">
<LockIcon className="w-12 h-12 mx-auto mb-4 text-gray-400" />
<h3 className="text-xl font-bold mb-2">Контент для членов клуба</h3>
<p className="text-gray-600 mb-4">
Получите NFT для доступа к этому разделу
</p>
<BuyNFTButton contractAddress={contractAddress} />
</div>
</div>
)}
</div>
);
}
Multi-Token Access
// Доступ если есть хотя бы один из нескольких токенов
async function checkMultiTokenAccess(walletAddress: string): Promise<{
hasAccess: boolean;
grantedBy?: string;
}> {
const gates = [
{ contract: PREMIUM_NFT, name: 'Premium NFT', type: 'ERC721' as const },
{ contract: GOVERNANCE_TOKEN, name: 'Governance Token', type: 'ERC20' as const, min: 1000n * 10n**18n }
];
for (const gate of gates) {
const has = gate.type === 'ERC721'
? await checkNFTOwnership(walletAddress, gate.contract)
: await checkERC20Balance(walletAddress, gate.contract, gate.min ?? 1n);
if (has) return { hasAccess: true, grantedBy: gate.name };
}
return { hasAccess: false };
}
Step-by-Step Guide to Implementing Token-Gating
Step 1: Define the Access Strategy
Analyze which content needs protection and choose the approach: Hard Gate for confidential data, Soft Gate for marketing, or Progressive Disclosure for engagement. Note that the number of blockchain checks affects TTFB: each validation adds 50–200 ms.
Step 2: Set Up Server-Side Verification
Implement middleware in Next.js App Router or Express that checks the JWT session token and requests token balance via an RPC provider. Optimize by caching results for 5 minutes — this reduces blockchain load by 60%.
Step 3: Integrate Client-Side Components
Wrap protected sections in the TokenGate or BlurGate component. To increase conversion, use Soft Gate on preview pages and Hard Gate for internal routes.
Step 4: Test Security
Verify that content is not served without authorization via direct URL and that session tokens cannot be forged. Perform load testing: the server should handle 1000 requests per minute with a latency of no more than 200 ms.
Work Process: From Audit to Deployment
| Stage | Duration | Result |
|---|---|---|
| Requirements Analysis | 1–2 days | Specification of tokens and strategy |
| Architecture | 1–2 days | Scheme of server verification and client components |
| Implementation | 2–4 days | Working prototype with protection |
| Testing | 1–2 days | Performance and security report |
| Deployment | 1 day | Production environment + monitoring |
Timeline: 4 to 10 days depending on complexity (number of tokens, multichain, integrations). Wallet integration: we connect MetaMask, WalletConnect, Coinbase Wallet, and others via the wagmi library. If necessary, custom integration for your project.
What's Included
- Requirements audit and strategy selection.
- Server-side route protection (Next.js/Nest.js/Express).
- UI components: TokenGate, BlurGate, WalletConnect Prompt.
- Integration with wallets (MetaMask, WalletConnect, Coinbase Wallet).
- Multi-token support (ERC-721, ERC-20, ERC-1155).
- Deployment and usage documentation.
- Client team training (2 hours).
- 30 days of support after delivery.
Timeline and Cost
Basic implementation (single token, server-side protection + blur gate) — 4–6 days. For projects with multi-token access, multichain, or complex business logic — up to 10 days. Cost is calculated individually after requirements analysis.
Get a consultation on your project today. Contact us for an audit — we will evaluate the project within 1 day and offer a fixed estimate. We guarantee security and performance. Order a security audit of your project — we will identify vulnerabilities within 24 hours.







