Search URLs need to balance user experience, shareability, and SEO. A well-designed search URL lets users bookmark, share, and refine their searches easily.
Key Takeaways
- 1Use q or query as the main search parameter
- 2Preserve search state in URL for shareability
- 3Handle special characters with proper encoding
- 4Consider whether search pages should be indexed
- 5Implement autocomplete with separate API endpoints
“Search URLs should be stateless and contain all information needed to reproduce the search results, enabling bookmarking and sharing of specific queries.”
Basic Search URLs
Most search implementations use a query parameter to pass the search term. The examples below show common patterns you will encounter across different types of sites.
# Common search URL patterns
# Simple query
/search?q=blue+shoes
/search?query=blue+shoes
# With filters
/search?q=shoes&color=blue&size=10&sort=price
# With pagination
/search?q=shoes&page=2&per_page=20
# Category-scoped search
/products/search?q=running
/category/shoes/search?q=nike
# Site search vs product search
/search?q=help+contact # Site-wide
/shop/search?q=running+shoes # Product searchSpaces in search queries are typically encoded as + signs or %20. Category-scoped searches narrow results before the query runs, giving users more relevant results within a specific section.
Beyond the main query, search URLs often include parameters for pagination, sorting, and filtering. Understanding these helps you build comprehensive search experiences.
Search Parameters
A complete search URL may include several parameters beyond the query itself. The table below lists common parameters and their purposes.
| Parameter | Purpose | Example |
|---|---|---|
| q / query | Search query | q=blue+shoes |
| page | Result page | page=2 |
| per_page / limit | Results per page | per_page=20 |
| sort | Sort order | sort=price_asc |
| type | Result type | type=products |
| scope | Search scope | scope=title |
Not all parameters need to appear in every URL. Use sensible defaults (page=1, sort=relevance) and only include parameters when they differ from defaults to keep URLs clean.
Implementation
Implementing search URLs requires parsing incoming URLs and building URLs as users refine their searches. The following code shows both directions.
// Search page with URL state
// Parse search URL
function parseSearchUrl(url) {
const params = new URLSearchParams(url.search);
return {
query: params.get('q') || '',
page: parseInt(params.get('page')) || 1,
perPage: Math.min(parseInt(params.get('per_page')) || 20, 100),
sort: params.get('sort') || 'relevance',
filters: {
category: params.get('category'),
priceMin: params.get('price_min'),
priceMax: params.get('price_max')
}
};
}
// Build search URL
function buildSearchUrl(params) {
const url = new URL('/search', window.location.origin);
if (params.query) {
url.searchParams.set('q', params.query);
}
if (params.page > 1) {
url.searchParams.set('page', params.page);
}
if (params.sort && params.sort !== 'relevance') {
url.searchParams.set('sort', params.sort);
}
// Add filters
Object.entries(params.filters || {}).forEach(([key, value]) => {
if (value) url.searchParams.set(key, value);
});
return url.toString();
}
// Update URL on search
function handleSearch(newQuery) {
const url = buildSearchUrl({
query: newQuery,
page: 1, // Reset to page 1
sort: currentSort,
filters: currentFilters
});
router.push(url);
}The buildSearchUrl function only adds parameters that differ from defaults, keeping URLs clean. When a new search is submitted, the page resets to 1 while preserving sort and filter preferences.
As users type, you typically want to show suggestions before they submit. Autocomplete requires a separate, lightweight API endpoint optimized for speed.
Autocomplete URLs
Autocomplete suggestions need to load quickly as users type. Use a separate endpoint that returns minimal data and debounce requests to avoid overwhelming your server.
// Autocomplete API endpoint
// GET /api/autocomplete?q=blu&limit=10
async function fetchAutocomplete(query) {
const url = new URL('/api/autocomplete', window.location.origin);
url.searchParams.set('q', query);
url.searchParams.set('limit', '10');
const response = await fetch(url);
return response.json();
}
// Debounced autocomplete
let debounceTimer;
function onSearchInput(query) {
clearTimeout(debounceTimer);
if (query.length < 2) {
setSuggestions([]);
return;
}
debounceTimer = setTimeout(async () => {
const results = await fetchAutocomplete(query);
setSuggestions(results);
}, 200);
}
// Autocomplete response
{
"suggestions": [
{ "text": "blue shoes", "type": "query" },
{ "text": "blue dress", "type": "query" },
{ "text": "Nike Blue Runner", "type": "product", "url": "/p/nike-blue-runner" }
]
}The 200ms debounce prevents firing a request on every keystroke. Suggestions include both query completions and direct product matches, letting users jump straight to relevant items.
Search queries often contain special characters that have meaning in URLs. Proper encoding prevents these characters from breaking your URLs or causing unexpected behavior.
Query Encoding
Characters like &, =, and + have special meaning in URLs. If a user searches for "C++ programming" or "rock & roll," you must encode these characters to preserve the intended query.
// Proper search query encoding
// User types: C++ programming
// URL should be: /search?q=C%2B%2B+programming
function encodeSearchQuery(query) {
// encodeURIComponent handles special chars
return encodeURIComponent(query);
}
// Common issues:
// + is space in query strings, so C++ becomes C (two spaces)
// Must encode + as %2B
// URL: /search?q=C%2B%2B+programming
// Decoded: C++ programming ✓
// Special characters to watch:
// & -> %26 (would start new parameter)
// = -> %3D (would be key=value separator)
// # -> %23 (would start fragment)
// + -> %2B (would be space)
// ? -> %3F (would start query string)
// Example with special chars
const query = 'rock & roll 50% off';
const url = `/search?q=${encodeURIComponent(query)}`;
// /search?q=rock%20%26%20roll%2050%25%20offAlways use encodeURIComponent when building URLs with user input. The browser will decode these values when the URL is accessed, restoring the original search query.
Search result pages present an SEO dilemma. You want search engines to find your content, but indexing thousands of search variations creates duplicate content issues.
SEO for Search Pages
Most internal search pages should not be indexed. They often duplicate category pages and create thin content for rare queries. Here are strategies to handle search pages for SEO.
<!-- Search page SEO options -->
<!-- Option 1: noindex all search pages -->
<meta name="robots" content="noindex, follow" />
<!-- Option 2: noindex with facets, index clean queries -->
<!-- /search?q=shoes - index -->
<!-- /search?q=shoes&color=blue - noindex -->
<!-- Option 3: Canonical to clean search URL -->
<link rel="canonical" href="https://shop.com/search?q=shoes" />
<!-- Internal search pages typically should NOT be indexed -->
<!-- Reasons: -->
<!-- 1. Duplicate content with category pages -->
<!-- 2. Low-quality thin pages for rare queries -->
<!-- 3. Waste of crawl budget -->
<!-- Exception: High-value search landing pages -->
<!-- Create dedicated pages for important queries -->
<!-- /shoes instead of /search?q=shoes -->For high-value search terms, create dedicated landing pages instead of relying on search results. A curated /shoes page will outrank /search?q=shoes every time.