Blog Sections Open
Fixing ?q= Duplicate URLs in Evolution CMS
A practical guide to removing duplicate friendly-URL variants in Evolution CMS and keeping the canonical URL as the only indexed version.
This article adapts an older community note from the modx.evo.im archive and updates it into a cleaner checklist for current projects.
If a site still exposes both the friendly URL and the legacy ?q= route, search engines can discover multiple addresses for the same page:
https://example.com/folder/page.html
https://example.com/?q=folder/page.html
https://example.com/index.php?q=folder/page.html
Those URLs should not stay indexable side by side. The public address must be the friendly URL, and all fallback variants should resolve to it with a 301 redirect.
What to check first
- Friendly URLs are enabled and working for real resource paths.
- The site does not leave
?q=versions accessible without a redirect. /index.phpdoes not behave like a second home page.- Repeated slashes such as
//or////are normalized.
Typical friendly URL rule
Many Evolution CMS projects still use the classic internal rewrite target:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php?q=$1 [L,QSA]
That internal rewrite is fine. The problem starts when public requests with ?q= are still reachable from the outside.
What the public behavior should be
https://example.com/?q=folder/page.html -> 301 -> https://example.com/folder/page.html
https://example.com/index.php?q=folder/page.html -> 301 -> https://example.com/folder/page.html
https://example.com/index.php -> 301 -> https://example.com/
https://example.com// or //// -> 301 -> normalized URL
Sample Apache rules
Exact rewrite rules always depend on the hosting stack and the rest of the project .htaccess, but the public logic usually looks like this:
# Redirect direct requests to /index.php?q=... to the friendly URL
RewriteCond %{THE_REQUEST} \s/+index\.php\?q=([^\s&]+) [NC]
RewriteRule ^index\.php$ /%1? [R=301,L,NE]
# Redirect direct requests to /?q=... to the friendly URL
RewriteCond %{THE_REQUEST} \s+/\?q=([^\s&]+) [NC]
RewriteRule ^$ /%1? [R=301,L,NE]
# Redirect a visible /index.php request to the home page
RewriteCond %{THE_REQUEST} \s/+index\.php(?:[\s?]|$) [NC]
RewriteRule ^index\.php$ / [R=301,L]
# Normalize duplicate slashes in public requests
RewriteCond %{THE_REQUEST} //+
RewriteRule ^(.*)$ /$1 [R=301,L]
Do not copy these rules blindly into production. Test them against your current routing, especially if the project already has redirects for language prefixes, trailing slashes, or custom manager paths.
Why this matters for SEO
- The crawler should see one stable URL per document.
- Internal links should point to the friendly version only.
- Duplicate URL variants waste crawl budget and split signals.
- If canonical tags exist, they should reinforce the final public URL, not fight against broken rewrite behavior.
Verification checklist
- Open the friendly URL and confirm it returns
200. - Open the
?q=andindex.php?q=variants and confirm they return a single301to the same friendly URL. - Check
/index.phpdirectly and confirm it resolves to the home page with301. - Check bad double-slash variants and confirm they normalize cleanly.
- Run a crawler such as Screaming Frog and confirm the duplicate variants disappear from the internal crawl.
Final note
This is one of those small infrastructure details that quietly damage SEO when left alone. It is worth fixing once and verifying as part of every Evolution CMS launch checklist.
Making RSS Feeds Use Absolute URLs for Yandex Turbo Pages
A feed-output note for Evolution CMS projects where Yandex Turbo pages reject relative links and require fully qualified absolute URLs in RSS content.
Previewing Images Inside Manager Input Fields
A small manager UX enhancement for Evolution CMS that shows the selected image directly inside the input field and enlarges it on hover.