<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Panagiotis Papoutsis</title>
    <description>The latest articles on DEV Community by Panagiotis Papoutsis (@tinosweb).</description>
    <link>https://dev.to/tinosweb</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4219358%2Fc6062ce2-7864-43c5-9492-3b6974a5f79f.png</url>
      <title>DEV Community: Panagiotis Papoutsis</title>
      <link>https://dev.to/tinosweb</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tinosweb"/>
    <language>en</language>
    <item>
      <title>Still Running Drupal 7 or Magento 1? A Practical Survival and Migration Plan</title>
      <dc:creator>Panagiotis Papoutsis</dc:creator>
      <pubDate>Sat, 31 Oct 2026 17:19:19 +0000</pubDate>
      <link>https://dev.to/tinosweb/still-running-drupal-7-or-magento-1-a-practical-survival-and-migration-plan-2fgm</link>
      <guid>https://dev.to/tinosweb/still-running-drupal-7-or-magento-1-a-practical-survival-and-migration-plan-2fgm</guid>
      <description>&lt;p&gt;Drupal 7 reached end of life in January 2025. Magento 1 got there back in June 2020. And yet, if you do client work for small businesses, you will keep running into both: a hotel site built a decade ago, a little shop that "still works", a cultural association whose original developer moved on years ago.&lt;/p&gt;

&lt;p&gt;A lot of my time goes into &lt;a href="https://tinosweb.gr/en/#services" rel="noopener noreferrer"&gt;maintaining websites for small businesses in the Cyclades&lt;/a&gt;, and "please look at our old site" is one of the most common requests I get. The owner usually wants two things at once: &lt;em&gt;don't break anything&lt;/em&gt; and &lt;em&gt;make it safe&lt;/em&gt;. Here's the plan I follow. It's deliberately boring, because boring is what you want when the codebase is older than some of your dependencies.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 0: Don't touch production. Get a copy.
&lt;/h2&gt;

&lt;p&gt;Before any upgrade discussion, get a full, reproducible copy of the site running locally or on a staging subdomain.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Files (exclude caches to keep the archive small)&lt;/span&gt;
&lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;--exclude&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'sites/default/files/css'&lt;/span&gt; &lt;span class="nt"&gt;--exclude&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'sites/default/files/js'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-czf&lt;/span&gt; site-files-&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%F&lt;span class="si"&gt;)&lt;/span&gt;.tar.gz public_html/

&lt;span class="c"&gt;# Database&lt;/span&gt;
mysqldump &lt;span class="nt"&gt;--single-transaction&lt;/span&gt; &lt;span class="nt"&gt;--routines&lt;/span&gt; &lt;span class="nt"&gt;--triggers&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-u&lt;/span&gt; dbuser &lt;span class="nt"&gt;-p&lt;/span&gt; dbname | &lt;span class="nb"&gt;gzip&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; db-&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%F&lt;span class="si"&gt;)&lt;/span&gt;.sql.gz
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On cheap shared hosting you may not have SSH. In that case the control panel's backup tool plus phpMyAdmin export will do, but check the dump actually restores. A backup you've never restored is just a hope.&lt;/p&gt;

&lt;p&gt;Keep at least one copy &lt;strong&gt;off the server&lt;/strong&gt;. When an old site gets compromised, the backups on the same account often go with it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Audit what's actually there
&lt;/h2&gt;

&lt;p&gt;Old sites accumulate modules, extensions and "temporary" fixes. Before planning anything, list them.&lt;/p&gt;

&lt;p&gt;For Drupal 7 with drush available:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;drush pm-list &lt;span class="nt"&gt;--type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;module &lt;span class="nt"&gt;--status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;enabled &lt;span class="nt"&gt;--no-core&lt;/span&gt;
drush up &lt;span class="nt"&gt;--security-only&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt;    &lt;span class="c"&gt;# what security updates were never applied?&lt;/span&gt;
drush core-status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For Magento 1, look at &lt;code&gt;app/etc/modules/*.xml&lt;/code&gt; for enabled modules and at &lt;code&gt;app/code/local&lt;/code&gt; and &lt;code&gt;app/code/community&lt;/code&gt; for custom and third-party code. Also check &lt;code&gt;app/design/frontend/&lt;/code&gt; for theme overrides; that's where most "small changes" from past years live.&lt;/p&gt;

&lt;p&gt;Write down three lists:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Features the business really uses&lt;/strong&gt; (contact form, booking enquiry, product catalogue, multilingual content).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Things nobody uses&lt;/strong&gt; (old modules, abandoned blog, test pages).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom code&lt;/strong&gt; that will need rewriting rather than migrating.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The second list is usually long, and it's good news: everything on it is something you don't have to migrate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Harden it while you plan
&lt;/h2&gt;

&lt;p&gt;Migration takes weeks. The site needs to survive in the meantime.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PHP version.&lt;/strong&gt; Many of these sites run on whatever PHP the host gave them in 2015. Check what your codebase supports and move to the newest version that works on staging first. Running an unsupported PHP version on top of an unsupported CMS doubles the risk.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lock down sensitive paths&lt;/strong&gt; in &lt;code&gt;.htaccess&lt;/code&gt; (Apache):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight apache"&gt;&lt;code&gt;&lt;span class="c"&gt;# Block Drupal 7 install/update scripts and the core text files that reveal&lt;/span&gt;
&lt;span class="c"&gt;# the version (robots.txt stays reachable). Unblock update.php while updating.&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nl"&gt;FilesMatch&lt;/span&gt;&lt;span class="sr"&gt; "^(install|update|cron|xmlrpc)\.php$|^(CHANGELOG|COPYRIGHT|INSTALL.*|LICENSE|MAINTAINERS|README|UPGRADE)\.txt$"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="nc"&gt;Require&lt;/span&gt; &lt;span class="ss"&gt;all&lt;/span&gt; denied
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nl"&gt;FilesMatch&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;
&lt;/span&gt;
&lt;span class="c"&gt;# Basic security headers&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nl"&gt;IfModule&lt;/span&gt;&lt;span class="sr"&gt; mod_headers.c&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="nc"&gt;Header&lt;/span&gt; &lt;span class="ss"&gt;always&lt;/span&gt; &lt;span class="ss"&gt;set&lt;/span&gt; X-Content-Type-Options "nosniff"
  &lt;span class="nc"&gt;Header&lt;/span&gt; &lt;span class="ss"&gt;always&lt;/span&gt; &lt;span class="ss"&gt;set&lt;/span&gt; X-Frame-Options "SAMEORIGIN"
  &lt;span class="nc"&gt;Header&lt;/span&gt; &lt;span class="ss"&gt;always&lt;/span&gt; &lt;span class="ss"&gt;set&lt;/span&gt; Referrer-Policy "strict-origin-when-cross-origin"
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nl"&gt;IfModule&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you block &lt;code&gt;cron.php&lt;/code&gt;, run cron from the server's crontab with drush instead. For Magento 1, make sure &lt;code&gt;app/etc/local.xml&lt;/code&gt; is not publicly readable and the admin URL isn't the default &lt;code&gt;/admin&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reduce the attack surface.&lt;/strong&gt; Disable unused modules, delete unused admin accounts, force strong passwords, and remove any &lt;code&gt;phpinfo.php&lt;/code&gt; or &lt;code&gt;test.php&lt;/code&gt; files left by previous developers. You'll be surprised how often they're there.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Consider extended support or a maintained fork.&lt;/strong&gt; For Drupal 7 there are commercial extended-support vendors listed on drupal.org; for Magento 1, the community-maintained OpenMage LTS project keeps patching the codebase. These buy time, not a future.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Pick the destination honestly
&lt;/h2&gt;

&lt;p&gt;Not every old site deserves a like-for-like migration.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Content-heavy Drupal 7 site with real structure&lt;/strong&gt; (content types, taxonomies, multilingual): migrate to current Drupal using the core Migrate API (&lt;code&gt;migrate_drupal&lt;/code&gt;, plus the contrib &lt;code&gt;migrate_upgrade&lt;/code&gt; and &lt;code&gt;migrate_plus&lt;/code&gt; modules). Content and users usually move well; custom modules and themes get rebuilt.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Magento 1 shop with a small catalogue&lt;/strong&gt;: a full Adobe Commerce / Magento Open Source 2 project can be overkill for a business selling a few dozen products. Evaluate lighter options honestly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Brochure site with five pages and a contact form&lt;/strong&gt;: often the right answer is a fresh, lightweight build. You copy the content, set up redirects, and you're done in a fraction of the time.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On Drupal, the migration itself looks like this once the source database is connected:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;drush migrate:upgrade &lt;span class="nt"&gt;--legacy-db-key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;drupal7 &lt;span class="nt"&gt;--configure-only&lt;/span&gt;
drush migrate:status
drush migrate:import &lt;span class="nt"&gt;--group&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;migrate_drupal_7
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it, check, roll back, adjust, repeat. Expect several passes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Protect the SEO you already have
&lt;/h2&gt;

&lt;p&gt;An old site often has years of backlinks and indexed URLs. Losing them is the most common migration regret.&lt;/p&gt;

&lt;p&gt;Export the URL list from the old sitemap or crawl it, then map every old URL to a new one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight apache"&gt;&lt;code&gt;&lt;span class="nc"&gt;RewriteEngine&lt;/span&gt; &lt;span class="ss"&gt;On&lt;/span&gt;
&lt;span class="nc"&gt;RewriteRule&lt;/span&gt; ^node/42$ /en/rooms [R=301,L]
&lt;span class="nc"&gt;RewriteRule&lt;/span&gt; ^catalog/product/view/id/(\d+)$ /products [R=301,L]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keep titles and meta descriptions for pages that rank, check &lt;code&gt;hreflang&lt;/code&gt; on multilingual sites, and submit the new sitemap in Google Search Console and Bing Webmaster Tools on launch day.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Leave it maintainable
&lt;/h2&gt;

&lt;p&gt;The reason the site got stuck on Drupal 7 or Magento 1 in the first place is usually that nobody owned updates. Before handing over:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use Composer for the new codebase and commit &lt;code&gt;composer.lock&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Automate backups (files and database), stored off-server.&lt;/li&gt;
&lt;li&gt;Write a one-page README: where it's hosted, how to deploy, how to restore.&lt;/li&gt;
&lt;li&gt;Agree who applies security updates, and how often.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;Legacy CMS work isn't glamorous, but for small businesses it's often the most valuable thing a developer can do: their site keeps working, gets safer, and ends up on a platform someone can maintain for the next ten years. If you're dealing with something similar for a local business in Greece, you'll find more about how I work at &lt;a href="https://tinosweb.gr/en/" rel="noopener noreferrer"&gt;tinosweb.gr&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  About the author
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Panagiotis Papoutsis&lt;/strong&gt; is a full-stack web developer with 10+ years of experience in PHP, Drupal, Magento and custom builds, plus SEO and web analytics. Based in Tinos, Greece, he runs Tinos Web, building and maintaining websites for small businesses and tourism brands across the Cyclades. More at &lt;a href="https://tinosweb.gr/en/" rel="noopener noreferrer"&gt;tinosweb.gr&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>drupal</category>
      <category>php</category>
      <category>webdev</category>
      <category>security</category>
    </item>
    <item>
      <title>A Technical SEO Checklist for Small Multilingual Business Websites</title>
      <dc:creator>Panagiotis Papoutsis</dc:creator>
      <pubDate>Sat, 31 Oct 2026 16:38:42 +0000</pubDate>
      <link>https://dev.to/tinosweb/a-technical-seo-checklist-for-small-multilingual-business-websites-10ip</link>
      <guid>https://dev.to/tinosweb/a-technical-seo-checklist-for-small-multilingual-business-websites-10ip</guid>
      <description>&lt;p&gt;Most of my work is building &lt;a href="https://tinosweb.gr/en/" rel="noopener noreferrer"&gt;small-business websites in Tinos, Greece&lt;/a&gt;: guesthouses, local shops, professionals and seasonal tourism businesses. These sites are small, often a handful of pages, but they're almost always &lt;strong&gt;multilingual&lt;/strong&gt;, because the visitors are Greek, German, French, Italian and more.&lt;/p&gt;

&lt;p&gt;Small multilingual sites have a specific set of technical SEO problems. None of them are hard, but they are easy to get wrong. Here's the checklist I go through before launch.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. One protocol, one host, one URL per page
&lt;/h2&gt;

&lt;p&gt;Pick a single canonical origin (for example &lt;code&gt;https://example.gr&lt;/code&gt;) and 301-redirect everything else to it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;http://example.gr&lt;/code&gt; → &lt;code&gt;https://example.gr&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;http://www.example.gr&lt;/code&gt; → &lt;code&gt;https://example.gr&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;https://www.example.gr&lt;/code&gt; → &lt;code&gt;https://example.gr&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Test all four variants with &lt;code&gt;curl -I&lt;/code&gt;. A surprisingly common failure is that the HTTPS version works perfectly while plain HTTP returns an error, often because a CDN or proxy in front of the server isn't configured to redirect. Old links, printed flyers and some crawlers still hit &lt;code&gt;http://&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then add a self-referencing canonical on every page:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"canonical"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://example.gr/en/"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also watch for duplicate entry points such as &lt;code&gt;/index.php&lt;/code&gt; returning &lt;code&gt;200&lt;/code&gt; instead of redirecting to &lt;code&gt;/&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Use language subfolders and real hreflang
&lt;/h2&gt;

&lt;p&gt;For small sites, subfolders (&lt;code&gt;/&lt;/code&gt;, &lt;code&gt;/en/&lt;/code&gt;, &lt;code&gt;/de/&lt;/code&gt;, &lt;code&gt;/fr/&lt;/code&gt;) are the simplest structure: one domain, one hosting setup, shared authority.&lt;/p&gt;

&lt;p&gt;Every language version should list &lt;strong&gt;all&lt;/strong&gt; alternates, including itself, plus an &lt;code&gt;x-default&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"alternate"&lt;/span&gt; &lt;span class="na"&gt;hreflang=&lt;/span&gt;&lt;span class="s"&gt;"el"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://example.gr/"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"alternate"&lt;/span&gt; &lt;span class="na"&gt;hreflang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://example.gr/en/"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"alternate"&lt;/span&gt; &lt;span class="na"&gt;hreflang=&lt;/span&gt;&lt;span class="s"&gt;"de"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://example.gr/de/"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"alternate"&lt;/span&gt; &lt;span class="na"&gt;hreflang=&lt;/span&gt;&lt;span class="s"&gt;"x-default"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://example.gr/"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Common mistakes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Alternates that aren't reciprocal (EN points to DE but DE doesn't point back).&lt;/li&gt;
&lt;li&gt;Wrong codes (&lt;code&gt;gr&lt;/code&gt; is a country, the language is &lt;code&gt;el&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Machine-translated pages with untranslated titles and meta descriptions.&lt;/li&gt;
&lt;li&gt;Setting &lt;code&gt;&amp;lt;html lang&amp;gt;&lt;/code&gt; incorrectly. Each version needs its own &lt;code&gt;lang="el"&lt;/code&gt;, &lt;code&gt;lang="en"&lt;/code&gt; and so on.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  3. Put hreflang in the sitemap too
&lt;/h2&gt;

&lt;p&gt;It's optional if the tags are in the HTML, but adding them to the sitemap makes the relationships explicit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;url&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;loc&amp;gt;&lt;/span&gt;https://example.gr/en/&lt;span class="nt"&gt;&amp;lt;/loc&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;xhtml:link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"alternate"&lt;/span&gt; &lt;span class="na"&gt;hreflang=&lt;/span&gt;&lt;span class="s"&gt;"el"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://example.gr/"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;xhtml:link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"alternate"&lt;/span&gt; &lt;span class="na"&gt;hreflang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://example.gr/en/"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/url&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reference the sitemap in &lt;code&gt;robots.txt&lt;/code&gt; and submit it in Google Search Console and Bing Webmaster Tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Don't stop at a one-page site
&lt;/h2&gt;

&lt;p&gt;A single long landing page is fine at launch, but it can only rank for so much. If you offer distinct services, give each one its own URL with its own title, H1 and meta description:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/services/web-design/
/services/local-seo/
/services/hosting/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Anchor links (&lt;code&gt;/#services&lt;/code&gt;) are great for navigation, but search engines index URLs, not page sections. Separate pages also give you something specific to link to when you write articles or build profiles elsewhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Get LocalBusiness schema right
&lt;/h2&gt;

&lt;p&gt;For a local business, JSON-LD structured data helps search engines connect your website with your physical presence:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"@context"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://schema.org"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"@type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ProfessionalService"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Example Studio"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://example.gr/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"telephone"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"+30..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"address"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"PostalAddress"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"addressLocality"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Tinos"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"addressCountry"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"GR"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sameAs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"https://www.facebook.com/..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"https://github.com/..."&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tips:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;strong&gt;one&lt;/strong&gt; primary business entity rather than several overlapping ones, and give it an &lt;code&gt;@id&lt;/code&gt; so other blocks can reference it.&lt;/li&gt;
&lt;li&gt;Fill &lt;code&gt;sameAs&lt;/code&gt; with your real profiles. An empty array is a missed opportunity.&lt;/li&gt;
&lt;li&gt;Only mark up reviews that are genuinely about the entity you're marking up. Testimonials about something else, or reviews collected and displayed on your own site about yourself, are a common reason review rich results get ignored or flagged.&lt;/li&gt;
&lt;li&gt;Validate with the Rich Results Test and the Schema.org validator.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  6. Write titles and descriptions per language, per page
&lt;/h2&gt;

&lt;p&gt;Titles should be written for humans in each language, not translated word for word. Keep the brand at the end, keep the separator consistent, and make sure the title tag matches your &lt;code&gt;og:title&lt;/code&gt; closely enough that shares look intentional.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Treat mobile performance as a feature
&lt;/h2&gt;

&lt;p&gt;Small business sites are usually built on themes that load several icon fonts, multiple Google Font families, carousels and animation libraries, often for features the page barely uses. Before launch:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Load only the font families and weights you actually use.&lt;/li&gt;
&lt;li&gt;Drop unused CSS and JS bundles.&lt;/li&gt;
&lt;li&gt;Serve images in modern formats at the displayed size, with &lt;code&gt;width&lt;/code&gt;/&lt;code&gt;height&lt;/code&gt; set and &lt;code&gt;loading="lazy"&lt;/code&gt; below the fold.&lt;/li&gt;
&lt;li&gt;Check server response time. A slow TTFB on shared hosting hurts every page.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Test with PageSpeed Insights on a mobile profile, and also on a real phone over mobile data.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Make the business verifiable off-site
&lt;/h2&gt;

&lt;p&gt;Technical SEO doesn't end on your domain. For local businesses, the most valuable step is usually a verified &lt;strong&gt;Google Business Profile&lt;/strong&gt; with the same name, phone and address as the website, plus &lt;strong&gt;Bing Places&lt;/strong&gt; and the main local directories. Consistent NAP (name, address, phone) data across the web is one of the clearest local signals you can send.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;None of this is advanced, but together it's the difference between a site that quietly sits there and one that search engines can understand, trust and show to the right people in the right language. If you want to see how I approach this for local businesses in the Cyclades, &lt;a href="https://tinosweb.gr/" rel="noopener noreferrer"&gt;my site&lt;/a&gt; is a good place to start.&lt;/p&gt;




&lt;h2&gt;
  
  
  About the author
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Panagiotis Papoutsis&lt;/strong&gt; is a full-stack web developer (PHP, Drupal, Magento, custom builds) with 10+ years of experience and a focus on SEO and web analytics. He runs Tinos Web, building websites and local SEO for small businesses in Tinos and the Cyclades. More at &lt;a href="https://tinosweb.gr/en/" rel="noopener noreferrer"&gt;tinosweb.gr&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>seo</category>
      <category>webdev</category>
      <category>html</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
