diff --git a/bridges/TelegramBridge.php b/bridges/TelegramBridge.php
index d650cb31..511f0263 100644
--- a/bridges/TelegramBridge.php
+++ b/bridges/TelegramBridge.php
@@ -24,7 +24,7 @@ class TelegramBridge extends BridgeAbstract
         'http://telegram.me/rssbridge' => ['username' => 'rssbridge'],
     ];
 
-    const CACHE_TIMEOUT = 900; // 15 mins
+    const CACHE_TIMEOUT = 60 * 15; // 15 mins
 
     private $feedName = '';
     private $enclosures = [];
diff --git a/formats/HtmlFormat.php b/formats/HtmlFormat.php
index 6c916de6..9ee4aeea 100644
--- a/formats/HtmlFormat.php
+++ b/formats/HtmlFormat.php
@@ -7,137 +7,56 @@ class HtmlFormat extends FormatAbstract
     public function stringify()
     {
         $extraInfos = $this->getExtraInfos();
-        $title = e($extraInfos['name']);
-        $uri = e($extraInfos['uri']);
-        $donationUri = e($extraInfos['donationUri']);
-        $donationsAllowed = Configuration::getConfig('admin', 'donations');
-
-        // Dynamically build buttons for all formats (except HTML)
         $formatFactory = new FormatFactory();
-
-        $buttons = '';
-        $links = '';
-
+        $buttons = [];
+        $linkTags = [];
         foreach ($formatFactory->getFormatNames() as $format) {
+            // Dynamically build buttons for all formats (except HTML)
             if ($format === 'Html') {
                 continue;
             }
-
-            $queryString = $_SERVER['QUERY_STRING'];
-            $query = str_ireplace('format=Html', 'format=' . $format, htmlentities($queryString));
-            $buttons .= sprintf('<a href="./?%s"><button class="rss-feed">%s</button></a>', $query, $format) . "\n";
-
-            $mime = $formatFactory->create($format)->getMimeType();
-            $links .= sprintf('<link href="./?%s" title="%s" rel="alternate" type="%s">', $query, $format, $mime) . "\n";
+            $formatUrl = '?' . str_ireplace('format=Html', 'format=' . $format, htmlentities($_SERVER['QUERY_STRING']));
+            $buttons[] = [
+                'href' => $formatUrl,
+                'value' => $format,
+            ];
+            $linkTags[] = [
+                'href' => $formatUrl,
+                'title' => $format,
+                'type' => $formatFactory->create($format)->getMimeType(),
+            ];
         }
 
-        if ($donationUri !== '' && $donationsAllowed) {
-            $str = sprintf(
-                '<a href="%s" target="_blank"><button class="highlight">Donate to maintainer</button></a>',
-                $donationUri
-            );
-            $buttons .= $str;
-            $str1 = sprintf(
-                '<link href="%s target="_blank"" title="Donate to Maintainer" rel="alternate">',
-                $donationUri
-            );
-            $links .= $str1;
+        if (Configuration::getConfig('admin', 'donations') && $extraInfos['donationUri'] !== '') {
+            $buttons[] = [
+                'href' => e($extraInfos['donationUri']),
+                'value' => 'Donate to maintainer',
+            ];
         }
 
-        $entries = '';
+        $items = [];
         foreach ($this->getItems() as $item) {
-            if ($item->getAuthor()) {
-                $entryAuthor = sprintf('<br /><p class="author">by: %s</p>', $item->getAuthor());
-            } else {
-                $entryAuthor = '';
-            }
-            $entryTitle = sanitize_html(strip_tags($item->getTitle()));
-            $entryUri = $item->getURI() ?: $uri;
-
-            $entryDate = '';
-            if ($item->getTimestamp()) {
-                $entryDate = sprintf(
-                    '<time datetime="%s">%s</time>',
-                    date('Y-m-d H:i:s', $item->getTimestamp()),
-                    date('Y-m-d H:i:s', $item->getTimestamp())
-                );
-            }
-
-            $entryContent = '';
-            if ($item->getContent()) {
-                $str2 = sprintf('<div class="content">%s</div>', sanitize_html($item->getContent()));
-                $entryContent = $str2;
-            }
-
-            $entryEnclosures = '';
-            if (!empty($item->getEnclosures())) {
-                $entryEnclosures = '<div class="attachments"><p>Attachments:</p>';
-
-                foreach ($item->getEnclosures() as $enclosure) {
-                    $template = '<li class="enclosure"><a href="%s" rel="noopener noreferrer nofollow">%s</a></li>';
-                    $url = sanitize_html($enclosure);
-                    $anchorText = substr($url, strrpos($url, '/') + 1);
-
-                    $entryEnclosures .= sprintf($template, $url, $anchorText);
-                }
-
-                $entryEnclosures .= '</div>';
-            }
-
-            $entryCategories = '';
-            if (!empty($item->getCategories())) {
-                $entryCategories = '<div class="categories"><p>Categories:</p>';
-
-                foreach ($item->getCategories() as $category) {
-                    $entryCategories .= '<li class="category">'
-                    . sanitize_html($category)
-                    . '</li>';
-                }
-
-                $entryCategories .= '</div>';
-            }
-
-            $entries .= <<<EOD
-
-<section class="feeditem">
-	<h2><a class="itemtitle" href="{$entryUri}">{$entryTitle}</a></h2>
-	{$entryDate}
-	{$entryAuthor}
-	{$entryContent}
-	{$entryEnclosures}
-	{$entryCategories}
-</section>
-
-EOD;
+            $items[] = [
+                'url'           => $item->getURI() ?: $extraInfos['uri'],
+                'title'         => $item->getTitle() ?? '(no title)',
+                'timestamp'     => $item->getTimestamp(),
+                'author'        => $item->getAuthor(),
+                'content'       => $item->getContent() ?? '',
+                'enclosures'    => $item->getEnclosures(),
+                'categories'    => $item->getCategories(),
+            ];
         }
 
-        $charset = $this->getCharset();
-        $toReturn = <<<EOD
-<!DOCTYPE html>
-<html>
-<head>
-	<meta charset="{$charset}">
-	<meta name="viewport" content="width=device-width, initial-scale=1.0" />
-	<title>{$title}</title>
-	<link href="static/HtmlFormat.css" rel="stylesheet">
-	<link rel="icon" type="image/png" href="static/favicon.png">
-	{$links}
-	<meta name="robots" content="noindex, follow">
-</head>
-<body>
-	<h1 class="pagetitle"><a href="{$uri}" target="_blank">{$title}</a></h1>
-	<div class="buttons">
-		<a href="./#bridge-{$_GET['bridge']}"><button class="backbutton">← back to rss-bridge</button></a>
-		{$buttons}
-	</div>
-{$entries}
-</body>
-</html>
-EOD;
-
+        $html = render_template(__DIR__ . '/../templates/html-format.html.php', [
+            'charset'   => $this->getCharset(),
+            'title'     => $extraInfos['name'],
+            'linkTags'  => $linkTags,
+            'uri'       => $extraInfos['uri'],
+            'buttons'   => $buttons,
+            'items'     => $items,
+        ]);
         // Remove invalid characters
         ini_set('mbstring.substitute_character', 'none');
-        $toReturn = mb_convert_encoding($toReturn, $this->getCharset(), 'UTF-8');
-        return $toReturn;
+        return mb_convert_encoding($html, $this->getCharset(), 'UTF-8');
     }
 }
diff --git a/lib/FormatInterface.php b/lib/FormatInterface.php
index 8f98d6e4..c0355804 100644
--- a/lib/FormatInterface.php
+++ b/lib/FormatInterface.php
@@ -42,7 +42,7 @@ interface FormatInterface
      * Return items
      *
      * @throws \LogicException if the items are not set
-     * @return array The items
+     * @return FeedItem[] The items
      */
     public function getItems();
 
diff --git a/lib/html.php b/lib/html.php
index 693504b0..1e852928 100644
--- a/lib/html.php
+++ b/lib/html.php
@@ -18,15 +18,26 @@ function render(string $template, array $context = []): string
     return render_template('base.html.php', $context);
 }
 
+/**
+ * Render template as absolute path or relative to templates folder.
+ * Do not pass user input in $template
+ */
 function render_template(string $template, array $context = []): string
 {
     if (isset($context['template'])) {
         throw new \Exception("Don't use `template` as a context key");
     }
+    $templateFilepath = __DIR__ . '/../templates/' . $template;
     extract($context);
     ob_start();
     try {
-        require __DIR__ . '/../templates/' . $template;
+        if (is_file($template)) {
+            require $template;
+        } elseif (is_file($templateFilepath)) {
+            require $templateFilepath;
+        } else {
+            throw new \Exception(sprintf('Unable to find template `%s`', $template));
+        }
     } catch (\Throwable $e) {
         ob_end_clean();
         throw $e;
diff --git a/templates/html-format.html.php b/templates/html-format.html.php
new file mode 100644
index 00000000..fc24f759
--- /dev/null
+++ b/templates/html-format.html.php
@@ -0,0 +1,90 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <meta charset="<?= $charset ?>">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
+    <title><?= e($title) ?></title>
+    <link href="static/HtmlFormat.css" rel="stylesheet">
+    <link rel="icon" type="image/png" href="static/favicon.png">
+
+	<?php foreach ($linkTags as $link): ?>
+        <link
+            href="<?= $link['href'] ?>"
+            title="<?= $link['title'] ?>"
+            rel="alternate"
+            type="<?= $link['type'] ?>"
+        >
+	<?php endforeach; ?>
+
+    <meta name="robots" content="noindex, follow">
+</head>
+<body>
+    <h1 class="pagetitle">
+        <a href="<?= e($uri) ?>" target="_blank">
+            <?= e($title) ?>
+        </a>
+    </h1>
+
+    <div class="buttons">
+        <a href="./#bridge-<?= $_GET['bridge'] ?>">
+            <button class="backbutton">← back to rss-bridge</button>
+        </a>
+
+        <?php foreach ($buttons as $button): ?>
+            <a href="<?= $button['href'] ?>">
+                <button class="rss-feed"><?= $button['value'] ?></button>
+            </a>
+        <?php endforeach; ?>
+
+    </div>
+
+    <?php foreach ($items as $item): ?>
+        <section class="feeditem">
+            <h2>
+                <a
+                    class="itemtitle"
+                    href="<?= e($item['url']) ?>"
+                ><?= strip_tags($item['title']) ?></a>
+            </h2>
+
+            <?php if ($item['timestamp']): ?>
+                <time datetime="<?= date('Y-m-d H:i:s', $item['timestamp']) ?>">
+                    <?= date('Y-m-d H:i:s', $item['timestamp']) ?>
+                </time>
+            <?php endif; ?>
+
+            <?php if ($item['author']): ?>
+                <br/>
+                <p class="author">by: <?= e($item['author']) ?></p>
+            <?php endif; ?>
+
+            <div class="content">
+                <?= sanitize_html($item['content']) ?>
+            </div>
+
+            <?php if ($item['enclosures']): ?>
+                <div class="attachments">
+                    <p>Attachments:</p>
+                    <?php foreach ($item['enclosures'] as $enclosure): ?>
+                        <li class="enclosure">
+                            <a href="<?= e($enclosure) ?>" rel="noopener noreferrer nofollow">
+                                <?= e(substr($enclosure, strrpos($enclosure, '/') + 1)) ?>
+                            </a>
+                        </li>
+                    <?php endforeach; ?>
+                </div>
+            <?php endif; ?>
+
+            <?php if ($item['categories']): ?>
+                <div class="categories">
+                    <p>Categories:</p>
+                    <?php foreach ($item['categories'] as $category): ?>
+                        <li class="category"><?= e($category) ?></li>
+                    <?php endforeach; ?>
+                </div>
+            <?php endif; ?>
+        </section>
+    <?php endforeach; ?>
+
+    </body>
+</html>