前言

Miniflux 是一个基于 Golang 的极简 RSS 阅读器 1,RSSHub 可以为不提供 RSS Feed 的站点提供开箱即用的 RSS 路由。

本文使用 docker compose 部署服务,RSSHub 不直接暴露任何端口(仅限容器内 Miniflux 服务使用),最后使用 Cloudflare Tunnel 访问部署好的 Miniflux 服务。

开始之前,先在服务器上 安装 最新的 Docker 和 docker compose 插件,以及用于 Cloudflare Tunnel 服务的 cloudflared

Miniflux Overview

部署

SSH 登录服务器,开始执行命令

# 创建目录,用于 Miniflux 相关容器的持久化数据存储
sudo mkdir -p container/miniflux && cd container/miniflux

# 新建 docker-compose.yml 文件
touch docker-compose.yml

编辑这个文件,注意修改管理员信息及数据库相关信息

version: "3.1"

services:
   miniflux:
     image: miniflux/miniflux:latest
     container_name: miniflux
     restart: unless-stopped
     ports:
       - "127.0.0.1:8080:8080"
     depends_on:
       - db
       - rsshub
     environment:
       - DATABASE_URL=postgres://miniflux:<数据库密码>@db/miniflux?sslmode=disable
       #- POLLING_FREQUENCY=45
       - RUN_MIGRATIONS=1  # 运行数据迁移
       - CREATE_ADMIN=1  # 启动时创建用户
       - ADMIN_USERNAME=<管理员用户名>
       - ADMIN_PASSWORD=<管理员用户密码>
       - POLLING_PARSING_ERROR_LIMIT=2
       - BASE_URL=https://rss.youdamin.tld/  # 替换为您的自定义域名,结尾需要斜杠
       #- CLEANUP_ARCHIVE_UNREAD_DAYS=60
       #- CLEANUP_ARCHIVE_READ_DAYS=45
       #- PROXY_IMAGES=none
       - PUID=1000
       - PGID=1000

   db:
     image: postgres:alpine
     container_name: postgres
     restart: unless-stopped
     environment:
       - POSTGRES_USER=miniflux  # 数据库用户名
       - POSTGRES_PASSWORD=<数据库密码>
     volumes:
       - ./miniflux-db:/var/lib/postgresql/data

   rsshub:
     image: diygod/rsshub:chromium-bundled
     container_name: rsshub
     restart: unless-stopped
     environment:
       - NODE_ENV=production
       - CACHE_TYPE=redis
       - REDIS_URL=redis://redis:6379/
       - CACHE_EXPIRE=3600  # 缓存过期时间,单位:秒
       - REQUEST_TIMEOUT=40000  # 请求超时时间,单位:毫秒
     depends_on:
       - redis

   redis:
     image: redis:alpine
     container_name: redis
     restart: always
     volumes:
       - ./rsshub-redis:/data

考虑到 Miniflux 容器不一定等到依赖服务(如 PostgreSQL 数据库)完全就绪才启动 2,第一次部署时,建议分两步启动容器

首先拉取所需的镜像 3 并启动依赖的数据库服务

sudo docker compose pull && \
sudo docker compose up -d db redis

等待大约 10 秒左右,开始第二步的容器启动

sudo docker compose up -d

查看日志没报错,服务应该已经就绪

sudo docker compose logs -f

访问

Cloudflare Zero Trust 控制台添加一个 Tunnel,配置自定义域名和监听地址

Tunnel Access

现在可以通过您的自定义域名访问刚才部署的 Miniflux 服务了

路由

常规的 RSS 订阅链接可以直接添加,要使用 RSSHub 支持的路由地址,先在 RSSHub 文档 上查找相应的路由路径。比如,准备订阅华尔街日报的中文新闻,可以看到路由路径是 /wsj/zh-cn/

RSSHub WSJ

那么在 Miniflux 添加的时候,路由域名写 http://rsshub:1200 就可以了

RSSHub Route

点击 查找源,Miniflux 应该可以正常获取订阅 4

Miniflux with RSSHub

完成

分享一个我自用的极简 CSS 样式,效果图在本文开头 🫣

:root {
  --font-family: -apple-system, system-ui, "San Francisco", "Helvetica Neue",
    Helvetica, Arial, "Microsoft Yahei", "WenQuanYi Micro Hei", "Noto Sans",
    sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol",
    "Noto Color Emoji";
  --body-color: #333;
  --body-background: #fff;
  --hr-border-color: #ccc;
  --title-color: #333;
  --link-color: rgb(30, 30, 30);
  --link-focus-color: red;
  --link-hover-color: #333;
  --link-visited-color: purple;
  --header-list-border-color: #ddd;
  --header-link-color: #444;
  --header-link-focus-color: #888;
  --header-link-hover-color: #888;
  --header-active-link-color: #444;
  --page-header-title-color: #333;
  --page-header-title-border-color: #333;
  --logo-color: #000;
  --logo-hover-color-span: #000;
  --table-border-color: #ddd;
  --table-th-background: #fcfcfc;
  --table-th-color: #333;
  --table-tr-hover-background-color: #f9f9f9;
  --table-tr-hover-color: #333;
  --button-primary-border-color: #3079ed;
  --button-primary-background: #4d90fe;
  --button-primary-color: #fff;
  --button-primary-focus-border-color: #2f5bb7;
  --button-primary-focus-background: #357ae8;
  --input-border: 1px solid #ccc;
  --input-background: #fff;
  --input-color: #333;
  --input-placeholder-color: #d0d0d0;
  --input-focus-color: #000;
  --input-focus-border-color: rgba(82, 168, 236, 0.8);
  --input-focus-box-shadow: 0 0 8px rgba(82, 168, 236, 0.6);
  --alert-color: #c09853;
  --alert-background-color: #fcf8e3;
  --alert-border-color: #fbeed5;
  --alert-success-color: #468847;
  --alert-success-background-color: #dff0d8;
  --alert-success-border-color: #d6e9c6;
  --alert-error-color: #b94a48;
  --alert-error-background-color: #f2dede;
  --alert-error-border-color: #eed3d7;
  --alert-info-color: #3a87ad;
  --alert-info-background-color: #d9edf7;
  --alert-info-border-color: #bce8f1;
  --panel-background: #fcfcfc;
  --panel-border-color: #ddd;
  --panel-color: #333;
  --modal-background: #f0f0f0;
  --modal-color: #333;
  --modal-box-shadow: 2px 0 5px 0 #ccc;
  --pagination-link-color: #333;
  --pagination-border-color: #ddd;
  --category-color: #090909;
  --category-background-color: #e8e8e8;
  --category-border-color: #e8e8e8;
  /* --category-link-color: #e8e8e8; */
  /* --category-link-hover-color: #e8e8e8; */
  --item-border-color: #ddd;
  --item-padding: 5px;
  --item-title-link-font-weight: 600;
  --item-status-read-title-link-color: #777;
  --item-status-read-title-focus-color: #777;
  --item-meta-focus-color: #777;
  --item-meta-li-color: #aaa;
  --current-item-border-width: 3px;
  --current-item-border-color: #bce;
  --current-item-box-shadow: none;
  --entry-header-border-color: #ddd;
  --entry-header-title-link-color: #333;
  --entry-content-color: #555;
  --entry-content-code-color: #333;
  --entry-content-code-background: #f0f0f0;
  --entry-content-code-border-color: #ddd;
  --entry-content-quote-color: #666;
  --entry-content-abbr-border-color: #999;
  --entry-enclosure-border-color: #333;
  --parsing-error-color: #333;
  --feed-parsing-error-background-color: #fcf8e3;
  --feed-parsing-error-border-style: solid;
  --feed-parsing-error-border-color: #f9e883;
  --feed-has-unread-background-color: #dfd;
  --feed-has-unread-border-style: solid;
  --feed-has-unread-border-color: #bee6bc;
  --category-has-unread-background-color: #dfd;
  --category-has-unread-border-style: solid;
  --category-has-unread-border-color: #bee6bc;
  --keyboard-shortcuts-li-color: #333;
  --counter-color: #666;
}

:root {
  --entry-content-font-weight: 300;
  --entry-content-font-family: -apple-system, system-ui, "San Francisco",
  "Helvetica Neue", Helvetica, Arial, "Microsoft Yahei", "WenQuanYi Micro Hei",
  "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji",
  "Segoe UI Symbol", "Noto Color Emoji";
  --entry-content-quote-font-family: var(--entry-content-font-family);
}

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

html {
  -webkit-text-size-adjust: 100%;
  -ms-text-size-adjust: 100%;
}

body {
  font-family: var(--font-family);
  text-rendering: optimizeLegibility;
  color: var(--body-color);
  background: var(--body-background);
}

hr {
  border: 0;
  height: 0;
  border-top: 1px dotted var(--hr-border-color);
  padding-bottom: 10px;
}

h1,
h2,
h3 {
  color: var(--title-color);
}

main {
  padding-left: 5px;
  padding-right: 5px;
  margin-bottom: 30px;
}

a {
  color: var(--link-color);
  box-shadow: 0 1px;
  text-decoration: none;

}

a:focus {
  outline: 0;
  color: var(--link-focus-color);
  text-decoration: none;
  outline: 1px dotted #aaa;
}

a:hover {
  color: var(--link-hover-color);
  text-decoration: none;
}

.header {
  margin-top: 10px;
  margin-bottom: 20px;
}

.header nav ul {
  display: none;
}

.header li {
  cursor: pointer;
  padding-left: 10px;
  line-height: 2.1em;
  font-size: 1.2em;
  border-bottom: 1px dotted var(--header-list-border-color);
}

.header li a:hover {
  color: #888;
}

.header a {
  font-size: 0.9em;
  color: var(--header-link-color);
  text-decoration: none;
  border: none;
  font-weight: 400;
}

.header .active a {
  color: var(--header-active-link-color);
  font-weight: 600;
}

.header a:hover {
  color: var(--header-link-hover-color);
}

.header a:focus {
  color: var(--header-link-focus-color);
}

.page-header {
  margin-bottom: 25px;
}

.page-footer {
  margin-bottom: 10px;
}

.page-header h1 {
  font-weight: 500;
  border-bottom: 1px dotted var(--page-header-title-border-color);
}

.page-header h1 a {
  text-decoration: none;
  color: var(--page-header-title-color);
}

.page-header h1 a:hover,
.page-header h1 a:focus {
  color: #666;
}

.page-header li,
.page-footer li {
  list-style-type: none;
  line-height: 1.8em;
  white-space: nowrap;
}

.page-header ul a .icon {
  padding-bottom: 2px;
}

.logo {
  cursor: pointer;
  text-align: center;
}

.logo a {
  color: var(--logo-color);
  letter-spacing: 1px;
}

.logo a:hover {
  color: #396;
}

.logo a span {
  color: #396;
}

.logo a:hover span {
  color: var(--logo-hover-color-span);
}

.search {
  text-align: center;
  display: none;
}

.search-toggle-switch {
  display: none;
}

#prompt-home-screen {
  display: none;
  position: fixed;
  bottom: 0;
  right: 0;
  width: 100%;
  text-align: center;
  background: #000;
  opacity: 85%;
}

#btn-add-to-home-screen {
  text-decoration: none;
  line-height: 30px;
  color: #fff;
}

#btn-add-to-home-screen:hover {
  color: red;
}

#toast-wrapper {
  visibility: hidden;
  opacity: 0;
  position: fixed;
  left: 0;
  bottom: 10%;
  color: #fff;
  width: 100%;
  text-align: center;
}

#toast-msg {
  background-color: rgba(0, 0, 0, 0.7);
  padding-bottom: 4px;
  padding-left: 4px;
  padding-right: 5px;
  border-radius: 5px;
}

.toast-animate {
  animation: toastKeyFrames 2s;
}

@keyframes toastKeyFrames {
  0% {
    visibility: hidden;
    opacity: 0;
  }

  25% {
    visibility: visible;
    opacity: 1;
    z-index: 9999;
  }

  50% {
    visibility: visible;
    opacity: 1;
    z-index: 9999;
  }

  75% {
    visibility: visible;
    opacity: 1;
    z-index: 9999;
  }

  100% {
    visibility: hidden;
    opacity: 0;
    z-index: 0;
  }
}

@media (min-width: 600px) {
  body {
    margin: auto;
    max-width: 750px;
  }

  .header {
    margin-bottom: 0;
  }

  .logo {
    text-align: left;
    float: left;
    margin-right: 15px;
    margin-left: 5px;
  }

  .header nav ul {
    display: block;
  }

  .header li {
    display: inline;
    padding: 0;
    padding-right: 15px;
    line-height: normal;
    border: none;
    font-size: 1em;
  }

  .page-header li,
  .page-footer li {
    display: inline;
    padding-right: 15px;
  }

  .search {
    text-align: right;
    display: block;
    margin-top: 10px;
    margin-right: 5px;
  }

  .search-toggle-switch {
    display: block;
  }

  .search-form {
    display: none;
  }

  .search-toggle-switch.has-search-query {
    display: none;
  }

  .search-form.has-search-query {
    display: block;
  }
}

table {
  width: 100%;
  border-collapse: collapse;
}

table,
th,
td {
  border: 1px solid var(--table-border-color);
}

th,
td {
  padding: 5px;
  text-align: left;
}

td {
  vertical-align: top;
}

th {
  background: var(--table-th-background);
  color: var(--table-th-color);
  font-weight: 400;
}

tr:hover {
  color: var(--table-tr-hover-color);
  background-color: var(--table-tr-hover-background-color);
}

.column-40 {
  width: 40%;
}

.column-25 {
  width: 25%;
}

.column-20 {
  width: 20%;
}

fieldset {
  border: 1px solid #ddd;
  padding: 8px;
}

legend {
  font-weight: 500;
  padding-left: 3px;
  padding-right: 3px;
}

label {
  cursor: pointer;
  display: block;
}

.radio-group {
  line-height: 1.9em;
}

div.radio-group label {
  display: inline-block;
}

select {
  margin-bottom: 15px;
}

input[type="search"],
input[type="url"],
input[type="password"],
input[type="text"],
input[type="number"] {
  color: var(--input-color);
  background: var(--input-background);
  border: var(--input-border);
  padding: 3px;
  line-height: 20px;
  width: 250px;
  font-size: 99%;
  margin-bottom: 10px;
  margin-top: 5px;
  -webkit-appearance: none;
}

input[type="search"]:focus,
input[type="url"]:focus,
input[type="password"]:focus,
input[type="text"]:focus,
input[type="number"]:focus {
  color: var(--input-focus-color);
  border-color: var(--input-focus-border-color);
  outline: 0;
  box-shadow: var(--input-focus-box-shadow);
}

#form-entries-per-page {
  max-width: 80px;
}

input[type="checkbox"] {
  margin-bottom: 15px;
}

textarea {
  width: 350px;
  color: var(--input-color);
  background: var(--input-background);
  border: var(--input-border);
  padding: 3px;
  margin-bottom: 10px;
  margin-top: 5px;
  -webkit-appearance: none;
}

textarea:focus {
  color: var(--input-focus-color);
  border-color: var(--input-focus-border-color);
  outline: 0;
  box-shadow: var(--input-focus-box-shadow);
}

input::placeholder {
  color: var(--input-placeholder-color);
  padding-top: 2px;
}

.form-label-row {
  display: flex;
}

.form-help {
  font-size: 0.9em;
  color: brown;
  margin-bottom: 15px;
}

.form-section {
  border-left: 2px dotted #ddd;
  padding-left: 20px;
  margin-left: 10px;
}

details > summary {
  outline: none;
  cursor: pointer;
}

.details-content {
  margin-top: 15px;
}

a.button {
  text-decoration: none;
}

.button {
  display: inline-block;
  -webkit-appearance: none;
  -moz-appearance: none;
  font-size: 1.1em;
  cursor: pointer;
  padding: 3px 10px;
  border: 1px solid;
  border-radius: unset;
}

.button-primary {
  border-color: var(--button-primary-border-color);
  background: var(--button-primary-background);
  color: var(--button-primary-color);
}

a.button-primary:hover,
a.button-primary:focus,
.button-primary:hover,
.button-primary:focus {
  border-color: var(--button-primary-focus-border-color);
  background: var(--button-primary-focus-background);
  color: var(--button-primary-color);
}

.button-danger {
  border-color: #b0281a;
  background: #d14836;
  color: #fff;
}

.button-danger:hover,
.button-danger:focus {
  color: #fff;
  background: #c53727;
}

.button:disabled {
  color: #ccc;
  background: #f7f7f7;
  border-color: #ccc;
}

.buttons {
  margin-top: 10px;
  margin-bottom: 20px;
}

.alert {
  padding: 8px 35px 8px 14px;
  margin-bottom: 20px;
  color: var(--alert-color);
  background-color: var(--alert-background-color);
  border: 1px solid var(--alert-border-color);
  border-radius: 4px;
  overflow: auto;
}

.alert h3 {
  margin-top: 0;
  margin-bottom: 15px;
}

.alert-success {
  color: var(--alert-success-color);
  background-color: var(--alert-success-background-color);
  border-color: var(--alert-success-border-color);
}

.alert-error {
  color: var(--alert-error-color);
  background-color: var(--alert-error-background-color);
  border-color: var(--alert-error-border-color);
}

.alert-error h3,
.alert-error a {
  color: var(--alert-error-color);
}

.alert-info {
  color: var(--alert-info-color);
  background-color: var(--alert-info-background-color);
  border-color: var(--alert-info-border-color);
}

.panel {
  color: var(--panel-color);
  background-color: var(--panel-background);
  border: 1px solid var(--panel-border-color);
  border-radius: 5px;
  padding: 10px;
  margin-bottom: 15px;
}

.panel h3 {
  font-weight: 500;
  margin-top: 0;
  margin-bottom: 20px;
}

.panel ul {
  margin-left: 30px;
}

template {
  display: none;
}

#modal-left {
  position: fixed;
  top: 0;
  left: 0;
  bottom: 0;
  width: 380px;
  overflow: auto;
  color: var(--modal-color);
  background: var(--modal-background);
  box-shadow: var(--modal-box-shadow);
  padding: 5px;
  padding-top: 30px;
}

#modal-left h3 {
  font-weight: 400;
  margin: 0;
}

.btn-close-modal {
  position: absolute;
  top: 0;
  right: 0;
  font-size: 1.7em;
  color: #ccc;
  padding: 0 0.2em;
  margin: 10px;
  text-decoration: none;
}

.btn-close-modal:hover {
  color: #999;
}

.keyboard-shortcuts li {
  margin-left: 25px;
  list-style-type: square;
  color: var(--keyboard-shortcuts-li-color);
  font-size: 0.95em;
  line-height: 1.45em;
}

.keyboard-shortcuts p {
  line-height: 1.9em;
}

.login-form {
  margin: 50px auto 0;
  max-width: 280px;
}

.unread-counter-wrapper,
.error-feeds-counter-wrapper {
  font-size: 0.9em;
  font-weight: 300;
  color: var(--counter-color);
}

.category {
  font-size: 0.75em;
  background-color: var(--category-background-color);
  border: 1px solid var(--category-border-color);
  border-radius: 5px;
  margin-left: 0.25em;
  padding: 1px 0.4em;
  white-space: nowrap;
  color: var(--category-color);
}

.category a {
  color: var(--category-link-color);
  text-decoration: none;
}

.category a:hover,
.category a:focus {
  color: var(--category-link-hover-color);
}

.pagination {
  font-size: 1.0em;
  display: flex;
  align-items: center;
}

.pagination-top {
  padding-bottom: 8px;
}

.pagination-bottom {
  padding-top: 8px;
}

.pagination-entry-top {
  padding-top: 8px;
}

.pagination-entry-bottom {
  border-top: 1px dotted var(--pagination-border-color);
  margin-bottom: 15px;
  margin-top: 50px;
  padding-top: 8px;
}

.pagination > div {
  flex: 1;
}

.pagination-next {
  text-align: right;
}

.pagination-prev:before {
  content: "« ";
}

.pagination-next:after {
  content: " »";
}

.pagination a {
  color: var(--pagination-link-color);
}

.pagination a:hover,
.pagination a:focus {
  text-decoration: none;
}

.item {
  border: 1px dotted var(--item-border-color);
  margin-bottom: 20px;
  padding: var(--item-padding);
  overflow: hidden;
}

.item.current-item {
  border: var(--current-item-border-width) solid
    var(--current-item-border-color);
  padding: 3px;
  box-shadow: var(--current-item-box-shadow);
}

.item-title a {
  text-decoration: none;
  font-weight: var(--item-title-link-font-weight);
}

.item-status-read .item-title a {
  color: var(--item-status-read-title-link-color);
}

.item-meta {
  color: var(--item-meta-focus-color);
  font-size: 0.8em;
}

.item-meta a {
  color: #777;
  text-decoration: none;
}

.item-meta a:hover,
.item-meta a:focus {
  color: #333;
}

.item-meta ul {
  margin-top: 5px;
}

.item-meta li {
  display: inline-block;
}

.item-meta-info {
  font-size: 0.85em;
}

.item-meta-info li:after {
  content: "|";
  color: var(--item-meta-li-color);
}

.item-meta-info li:last-child:after {
  content: "";
}

.item-meta-icons li {
  margin-right: 8px;
  margin-top: 4px;
}

.item-meta-icons li:last-child {
  margin-right: 0;
}

.items {
  overflow-x: hidden;
  touch-action: pan-y;
}

.hide-read-items .item-status-read:not(.current-item) {
  display: none;
}

.entry-swipe {
  transition-property: transform;
  transition-duration: 0s;
  transition-timing-function: ease-out;
}

article.feed-parsing-error {
  background-color: var(--feed-parsing-error-background-color);
  border-style: var(--feed-parsing-error-border-style);
  border-color: var(--feed-parsing-error-border-color);
}

article.feed-has-unread {
  background-color: var(--feed-has-unread-background-color);
  border-style: var(--feed-has-unread-border-style);
  border-color: var(--feed-has-unread-border-color);
}

.parsing-error {
  font-size: 0.85em;
  margin-top: 2px;
  color: var(--parsing-error-color);
}

.parsing-error-count {
  cursor: pointer;
}

article.category-has-unread {
  background-color: var(--category-has-unread-background-color);
  border-style: var(--category-has-unread-border-style);
  border-color: var(--category-has-unread-border-color);
}

.icon,
.icon-label {
  vertical-align: middle;
  display: inline-block;
  padding-right: 2px;
}

.icon {
  width: 16px;
  height: 16px;
}

.entry header {
  padding-bottom: 5px;
  border-bottom: 1px dotted var(--entry-header-border-color);
}

.entry header h1 {
  font-size: 2em;
  line-height: 1.25em;
  margin: 5px 0 30px;
}

.entry header h1 a {
  text-decoration: none;
  color: var(--entry-header-title-link-color);
}

.entry header h1 a:hover,
.entry header h1 a:focus {
  color: #666;
}

.entry-actions {
  margin-bottom: 20px;
}

.entry-actions a {
  text-decoration: none;
}

.entry-actions li {
  display: inline-block;
  margin-right: 15px;
  line-height: 1.7em;
}

.entry-meta {
  font-size: 0.95em;
  margin: 0 0 20px;
  color: #666;
  overflow-wrap: break-word;
}

.entry-website img {
  vertical-align: top;
}

.entry-website a {
  color: #666;
  vertical-align: top;
  text-decoration: none;
}

.entry-website a:hover,
.entry-website a:focus {
  text-decoration: underline;
}

.entry-date {
  font-size: 0.65em;
  font-style: italic;
  color: #555;
}

.entry-content {
  padding-top: 15px;
  font-size: 1.2em;
  font-weight: var(--entry-content-font-weight);
  font-family: var(--entry-content-font-family);
  color: var(--entry-content-color);
  line-height: 1.4em;
  overflow-wrap: break-word;
}

.entry-content h1,
h2,
h3,
h4,
h5,
h6 {
  margin-top: 15px;
  margin-bottom: 10px;
}

.entry-content iframe,
.entry-content video,
.entry-content img {
  max-width: 100%;
}

.entry-content figure {
  margin-top: 15px;
  margin-bottom: 15px;
}

.entry-content figure img {
  border: 1px solid #000;
}

.entry-content figcaption {
  font-size: 0.75em;
  text-transform: uppercase;
  color: #777;
}

.entry-content p {
  margin-top: 10px;
  margin-bottom: 15px;
}

.entry-content a {
  overflow-wrap: break-word;
}

.entry-content a:visited {
  color: var(--link-visited-color);
}

.entry-content dt {
  font-weight: 500;
  margin-top: 15px;
  color: #555;
}

.entry-content dd {
  margin-left: 15px;
  margin-top: 5px;
  padding-left: 20px;
  border-left: 3px solid #ddd;
  color: #777;
  font-weight: 300;
  line-height: 1.4em;
}

.entry-content blockquote {
  border-left: 4px solid #ddd;
  padding-left: 25px;
  margin-left: 20px;
  margin-top: 20px;
  margin-bottom: 20px;
  line-height: 1.4em;
  font-family: var(--entry-content-quote-font-family);
}

.entry-content q {
  color: var(--entry-content-quote-color);
  font-family: var(--entry-content-quote-font-family);
  font-style: italic;
}

.entry-content q:before {
  content: "“";
}

.entry-content q:after {
  content: "”";
}

.entry-content pre {
  padding: 5px;
  overflow: auto;
  overflow-wrap: initial;
  border-width: 1px;
  border-style: solid;
}

.entry-content pre,
.entry-content code {
  color: var(--entry-content-code-color);
  background: var(--entry-content-code-background);
  border-color: var(--entry-content-code-border-color);
}

.entry-content table {
  max-width: 100%;
}

.entry-content ul,
.entry-content ol {
  margin-left: 30px;
  margin-top: 15px;
  margin-bottom: 15px;
}

.entry-content li ul,
.entry-content li ol {
  margin-top: 0;
  margin-bottom: 0;
}

.entry-content ul {
  list-style-type: square;
}

.entry-content strong {
  font-weight: 600;
}

.entry-content sub,
.entry-content sup {
  font-size: 75%;
  line-height: 0;
  position: relative;
  vertical-align: baseline;
}

.entry-content sub {
  bottom: -0.25em;
}

.entry-content sup {
  top: -0.5em;
}

.entry-content abbr {
  cursor: pointer;
  text-decoration: none;
  border-bottom: 1px dashed var(--entry-content-abbr-border-color);
}

details.entry-enclosures {
  margin-top: 25px;
}

.entry-enclosures summary {
  font-weight: 500;
  font-size: 1.2em;
}

.entry-enclosure {
  border: 1px dotted var(--entry-enclosure-border-color);
  padding: 5px;
  margin-top: 10px;
  max-width: 100%;
}

.entry-enclosure-download {
  font-size: 0.85em;
  overflow-wrap: break-word;
}

.enclosure-video video,
.enclosure-image img {
  max-width: 100%;
}

.confirm {
  font-weight: 500;
  color: #ed2d04;
}

.confirm a {
  color: #ed2d04;
}

.loading {
  font-style: italic;
}

.bookmarklet {
  border: 1px dashed #ccc;
  border-radius: 5px;
  padding: 15px;
  margin: 15px;
  text-align: center;
}

.bookmarklet a {
  font-weight: 600;
  text-decoration: none;
  font-size: 1.2em;
}

.disabled {
  opacity: 20%;
}

Miniflux 简洁的外表下有着丰富的自定义功能和拓展,比如推送文章更新到 Telegram、使用 API Key 和各种 RSS 客户端集成、多用户支持……请尽情探索吧!


  1. 简洁但不简单,高度可定制性和功能拓展 ↩︎

  2. 环境变量里设置了首次启动 Miniflux 会初始化管理员用户,若数据库还未就绪,会出现初始化失败 ↩︎

  3. 文中的所有镜像都支持 x64 和 ARM64 架构 ↩︎

  4. 大多数网站(特别是国内网站)防爬虫策略过于严格,RSSHub 本身路由问题或是服务器 IP 信誉太差都可能会导致 RSS 路由失败 ↩︎