After a rebuild, every old URL that other sites link to needs a 301 to its new home in one hop. Old URLs are often dynamic, and neither RewriteRule nor Redirect sees the query string, so the match moves into RewriteCond %{QUERY_STRING}, whose captures come back as %1:
# /var/www/example/.htaccess: old URLs to their new homes, in one hop each
# Needs: mod_rewrite; AllowOverride FileInfo; Options FollowSymLinks;
# RewriteMap moved "txt:/etc/apache2/moved-urls.txt" in the vhost
RewriteEngine On
# /shop.php?product=42&utm_source=x -> /products/42, old query string dropped
RewriteCond %{QUERY_STRING} (?:^|&)product=(\d+)(?:&|$)
RewriteRule ^shop\.php$ /products/%1 [R=301,L,QSD]
# Hundreds of one-off moves live in the map, not in hundreds of rules
RewriteCond ${moved:%{REQUEST_URI}} ^(/.+)$
RewriteRule ^ %1 [R=301,L]
# Sections that are gone for good
RewriteRule ^forum/ - [G]Line 6 finds product anywhere in the query string, and QSD (2.4.0 and later) discards the old string instead of appending it. Line 9 looks the path up in a text file of old new pairs, one per line, cached until the file changes (RewriteMap); a miss returns an empty string, so the condition fails. Line 12 answers 410. A rule that matches index.php needs the REDIRECT_STATUS guard of Slashes and Duplicates once a front controller sits below it.
/shop.php?product=42&utm_source=news 301 http://example.com/products/42 /old/about-us.html 301 http://example.com/about /old/missing.html 404 /forum/t/9 410
Test your analytics' list of old URLs this way before launch, and keep the redirects at least a year, as Google advises for site moves.